Jump to content

Need some help with science transfer plugin


Recommended Posts

I've been trying to make a plugin to automatically collect experiment data and move it to a science container.

Nothing works. I can't even get the print() to show in the log. I'm terrible with lists and foreach statements, which is part of the reason why I'm pushing myself to do this, but no matter how I change things, nothing seems to work. No print output, no collection to a container. Although the nose cone experiment on my vessel appears to be having it's data fetched. That is the only sign of life I get from the plugin.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using UnityEngine;


namespace ForScience
{
[KSPAddon(KSPAddon.Startup.Flight, false)]
public class ForScience : Part
{
public void getScience()
{


Vessel activeVessel = FlightGlobals.ActiveVessel;
ScienceData[] data = null;
ModuleScienceContainer container = null;

List<Part> parts = activeVessel.parts;
foreach (Part part in parts)
{
foreach (PartModule partModules in Modules)
{
ModuleScienceExperiment experiment = partModules as ModuleScienceExperiment;
data = experiment.GetData();

}

foreach (PartModule partModules in Modules)
{
if (partModules is ModuleScienceContainer & container == null)
{
container = partModules as ModuleScienceContainer;
int i = 0;
for (i = 0; i < data.Length; i++)
{
container.AddData(data[i]);
container.RemoveData(data[i]);
}
}

}

}

}

public void OnUpdate()
{
getScience();
print("please work");
}

}

}

Any help would be appreciated.

Link to comment
Share on other sites

Don't use OnUpdate(). It's only called when the Part is active (Staged) just use Update() and FixedUpdate() :)

Yay! Error Messages! Now I can actually debug. :)

If I could give you a hug, I would. Instead I'll have to settle for giving you some internet points. :)

Link to comment
Share on other sites

Yay! Error Messages! Now I can actually debug. :)

If I could give you a hug, I would. Instead I'll have to settle for giving you some internet points. :)

No problem :)

OnUpdate() and OnFixedUpdate() are usefull for stuff that deals with staging, but apart from that there's no need to use them. I stopped using them for realchute because the staging controller is totally dysfunctional and catching the staging keypress and switching a bool is more reliable but shh.

Link to comment
Share on other sites

Alrighty, I bashes my head against these problems long enough.

The plugin seems to work...on the first vessel loaded. Reloading the vessel to launch pad or exiting back out and using a different vessel causes no apparent activity from the plugin, and no errors are thrown.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using UnityEngine;


namespace ForScience
{
[KSPAddon(KSPAddon.Startup.Flight, true)]
public class ForScience : MonoBehaviour
{
ModuleScienceExperiment experiment = null;
ModuleScienceContainer container = null;
ScienceData[] data = null;

void Update()
{
Debug.Log("[For Science] initialized at " + experiment.name + container.name + data);
foreach (Part part in FlightGlobals.ActiveVessel.parts)
{
Debug.Log("[For Science] searching parts");
foreach (PartModule partModules in part.Modules)
{
Debug.Log("[For Science] searching modules");
if (partModules is ModuleScienceContainer)
{
container = partModules as ModuleScienceContainer;
Debug.Log("[For Science] found container: " + container);
}
else if (partModules is ModuleScienceExperiment)
{
experiment = partModules as ModuleScienceExperiment;
Debug.Log("[For Science] found experiment: " + experiment);
if (experiment.GetScienceCount() > 0)
{
Debug.Log("[For Science] Collecting: " + experiment.name);
data = experiment.GetData();
container.AddData(data[0]);
experiment.ResetExperiment();

}
}
}
}
}
}
}

I also have a secondary problem in that the plugin stores multiple copies of the experiments even though the experiments are supposed to be reset and presumably cleared after the data is stored into ScienceData[] and only pulled from there into the container once. I've run up to 8 experiments and has it store 32 data in the pod. Obviously with 3 duplicate data for each experiment. I've tried transferring the data to container directly, but I get type conversion errors in the compiler.

Edited by WaveFunctionP
Link to comment
Share on other sites

I also have a secondary problem in that the plugin stores multiple copies of the experiments even though the experiments are supposed to be reset and presumably cleared after the data is stored into ScienceData[] and only pulled from there into the container once. I've run up to 8 experiments and has it store 32 data in the pod. Obviously with 3 duplicate data for each experiment. I've tried transferring the data to container directly, but I get type conversion errors in the compiler.

The only-works-once problem is, I think, due to that "true" at the end of the "[KSPAddon" line at the top. I think that should be false, so that it loads up every time you go to the flight scene.

The duplicate science problem might have something to do with how you're looping through every part to find containers and experiments. It seems like it might choke if it found the experiment before the container, or it might find multiple containers and send science data to different containers based on the part order of your vessel. I'm not really sure why any of this would send duplicate results though. Maybe you also need to clear out the data[] array after each transfer, too.

Link to comment
Share on other sites

I also have a secondary problem in that the plugin stores multiple copies of the experiments even though the experiments are supposed to be reset and presumably cleared after the data is stored into ScienceData[] and only pulled from there into the container once. I've run up to 8 experiments and has it store 32 data in the pod. Obviously with 3 duplicate data for each experiment. I've tried transferring the data to container directly, but I get type conversion errors in the compiler.

Vessel has a handy function called FindPartModulesImplementing<T> that can be used to greatly simplify your code:

var containers = FlightGlobals.ActiveVessel.FindPartModulesImplementing<ModuleScienceContainer>();
var sourceData = FlightGlobals.ActiveVessel.FindPartModulesImplementing<ModuleScienceExperiment>().Cast<IScienceDataContainer>().ToList();

containers[0].StoreData(sourceData, true);

This works fine for the only experiment I tested, the gravioli detector. I'm not sure resetting experiments is necessary (it wasn't for the one I tested) but I'll leave that to you. Good luck

Link to comment
Share on other sites

Another thing to add. If manually resetting the experiment is necessary you should be using ModuleScienceExperiment.DumpData() (or maybe the IScienceDataContainer interface's method) not ResetExperiment(). The latter is used for throwing out data, either through the experiment results page, the reset Event/Action, or the EVA reset. Dumpdata() is what is called after you transmit something and it will properly set the experiment as inoperable if need be.

Link to comment
Share on other sites

Thank you, to all of you.

The setting true to false on the kspaddon line did the trick for swapping vessels.

I had tried using the isciencecontainer, but didn't know how to use it. It works perfectly for removing and setting experiments non-repeatable experiments inoperable. I was using dumpdata() and/or resetexperiment() just to try and stop duplicates. There's still so much I need to learn about programming. I was trying to cast using something like (ThingIWantToCastTo) thingIWantRecast.

That FindPartModulesImplimenting method is a godsend.

Now I can hopefully start working on features and adding a gui.

Thanks again.

Edited by WaveFunctionP
Link to comment
Share on other sites

Alrighty, so I've been developing some logic to run experiments if there is new data to be had and we haven't already collected it onboard.

The problem I'm having is that the all of the situation/biome functions that I've tried aren't returning the correct values for the launchpad, and presumably elsewhere at KSP. Instead the return shores. It's driving me batty, because I feel like I'm so close to being able to add this functionality in a very complete way. As usual, I expect that I'm probably doing it the hard way. (You can ignore the commented sections, I was jumping through hoops trying to check experiment results without actually running the experiment and run them against what is stored in the container.)

The debugs will return [email protected]pad from the existing data stored in container (as that is what the actual experiment as returned), and [email protected] from the "currentfunctions", be it via the vessel.situations or from getexperimentsituation.

var currentBody = vessel.mainBody;
var currentBiome = ScienceUtil.GetExperimentBiome(currentBody, vessel.latitude, vessel.longitude);
var currentSituation = ScienceUtil.GetExperimentSituation(vessel);

var experimentList = vessel.FindPartModulesImplementing<ModuleScienceExperiment>();
foreach (ModuleScienceExperiment experiment in experimentList)
{
ScienceExperiment currentExperimentID = ResearchAndDevelopment.GetExperiment(experiment.experimentID);
ScienceSubject checkExisting = ResearchAndDevelopment.GetExperimentSubject(currentExperimentID, currentSituation, currentBody, currentBiome);


bool dataIsInContainer = false;
foreach (ScienceData containerData in container.GetData())
{
if (containerData.subjectID == experiment.experimentID) dataIsInContainer = true;
Debug.Log("container: " + containerData.subjectID);
Debug.Log("experiment: " + checkExisting.id);
}


if (checkExisting.scientificValue > 0f & !dataIsInContainer)
{
Debug.Log("dataisincontainer = " + dataIsInContainer);

//experiment.DeployExperiment();
//ScienceData[] currentExperimentData = experiment.GetData();
//if (data.Contains(currentExperimentData[0])) experiment.DumpData(currentExperimentData[0]);
//else
//{
// container.AddData(currentExperimentData[0]);
// experiment.DumpData(currentExperimentData[0]);

//}
}
}
container.StoreData(experimentList.Cast<IScienceDataContainer>().ToList(), true);

I can concoct a string if needed (and I've tried) , but I need a function that will return the correct value for situation. Otherwise, my container check will fail and won't run experiments or will run duplicates and spam the user with duplicate reports.

I hate making special cases, and I suspect I'm doing this wrong, because it seems like it should be such a simple thing to check if you have the data already.

Link to comment
Share on other sites

This is what I use to check the biome.


public string getBiome(ExperimentSituations s)
{
if (scienceExp.BiomeIsRelevantWhile(s))
{
switch (vessel.landedAt)
{
case "LaunchPad":
return vessel.landedAt;
case "Runway":
return vessel.landedAt;
case "KSC":
return vessel.landedAt;
default:
return FlightGlobals.currentMainBody.BiomeMap.GetAtt(vessel.latitude * Mathf.Deg2Rad, vessel.longitude * Mathf.Deg2Rad).name;
}
}
else return "";
}

Though looking at it, it could probably be simpler. vessel.landedAt returns an empty string (I think) anywhere outside of the KSC, so you could just check to see if that is empty, if not return that value for the biome, and if it is do the regular biome check. I've heard that you can also get results for the "KSC" biome while flying above it (or maybe in space if you're really quick?), but I don't know how that works or how to replicate it.

Do you get accurate biome results using vessel.latitude/longitude? Using the method I showed always requires position in radians not degrees.

As for the experimental situation, check around. I consider it a bug that you can only conduct atmospheric science while on a sub-orbital trajectory, so all of my experiments use a method that returns flying high/low anytime you are in the atmosphere. All of the methods I've seen are basically the same as mine, there are just a few adjustments that need to be made to return stock science behavior.

Ignore the bit about asteroids at the top...


public ExperimentSituations getSituation()
{
//Check for asteroids, return values that should sync with existing parts
if (asteroidReports && AsteroidScience.asteroidGrappled()) return ExperimentSituations.SrfLanded;
if (asteroidReports && AsteroidScience.asteroidNear()) return ExperimentSituations.InSpaceLow;
switch (vessel.situation)
{
case Vessel.Situations.LANDED:
case Vessel.Situations.PRELAUNCH:
return ExperimentSituations.SrfLanded;
case Vessel.Situations.SPLASHED:
return ExperimentSituations.SrfSplashed;
default:
if (vessel.altitude < vessel.mainBody.maxAtmosphereAltitude && vessel.mainBody.atmosphere)
{
if (vessel.altitude < vessel.mainBody.scienceValues.flyingAltitudeThreshold)
return ExperimentSituations.FlyingLow;
else
return ExperimentSituations.FlyingHigh;
}
if (vessel.altitude < vessel.mainBody.scienceValues.spaceAltitudeThreshold)
return ExperimentSituations.InSpaceLow;
else
return ExperimentSituations.InSpaceHigh;
}
}

Edit: If you want a good look at how to check experiment status for stock science look at the code for xEvilReeperx's ScienceAlert, last I checked it had the methods you need for these kinds of things.

Edited by DMagic
Link to comment
Share on other sites

Thanks, I'll be sure to check that out.

I get what seems to be accurate results for biomes. The problem was that at KSC it would return shores. I simply pass the appropriate FlightGlobal.ActiveVessel parameters directly into ScienceUtil.GetExperimentBiome(). From there I can get a lot of parameters from ResearchAndDevelopment. No math or casts involved. No idea if that is optimal or not.

I ended up doing a check for landed and concocting a string together with vessel.landedAt. Seems to work.

After that, a quick test shows that it immediately picks up on biome and situation changes.

New problem is that deployexperiment seems to always run twice. I've checked and it isn't looping twice. It is simply returning double results, which pops up an annoying message box. So each time I hit a new biome, I have click to close it. I've been looking around in the library, but I don't see any reference to this window. Would be nice to hide the results dialogs, or have my routine pause and only collect data when I click save, but atleast it's working now.

Here's what I'm doing currently. I may need to handle the landedat better if it does return null.

private void TransferScience()
{
Vessel vessel = FlightGlobals.ActiveVessel;
var containerList = vessel.FindPartModulesImplementing<ModuleScienceContainer>();
if (container == null) container = containerList[0];
data = container.GetData();

var currentBody = vessel.mainBody;
var currentBiome = ScienceUtil.GetExperimentBiome(currentBody, vessel.latitude, vessel.longitude);
var currentSituation = ScienceUtil.GetExperimentSituation(vessel);

var experimentList = vessel.FindPartModulesImplementing<ModuleScienceExperiment>();
foreach (ModuleScienceExperiment experiment in experimentList)
{
ScienceExperiment currentExperimentID = ResearchAndDevelopment.GetExperiment(experiment.experimentID);
ScienceSubject currentSubject = ResearchAndDevelopment.GetExperimentSubject(currentExperimentID, currentSituation, currentBody, currentBiome);

bool dataIsInContainer = false;
foreach (ScienceData containerData in container.GetData())
{
if (vessel.Landed)
{
currentSubject.id = experiment.experimentID + "@" + vessel.mainBody.name + currentSituation + vessel.landedAt;
}
if (containerData.subjectID == currentSubject.id) dataIsInContainer = true;

Debug.Log("landedAt: " + vessel.landedAt);
Debug.Log("currentSituation: " + currentSituation);
Debug.Log("container: " + containerData.subjectID);
Debug.Log("experiment: " + currentSubject.id);
Debug.Log("dataisincontainer = " + dataIsInContainer);
}


if (currentSubject.scientificValue > 0f & !dataIsInContainer)
{
Debug.Log("dataisincontainer = " + dataIsInContainer);

experiment.DeployExperiment();

container.StoreData(experimentList.Cast<IScienceDataContainer>().ToList(), true);
}
}
}

edit: Well, this is fun. When I recover the vessel, none of the data is recovered. Back to the drawing board.

Edited by WaveFunctionP
Link to comment
Share on other sites

Do I get a medal for breaking an install?

edit: Yup, something in there is breaking the whole install.

definitely have to start over from scratch, there's no indication as to how or why data recovery gets broken.

edit: I suspect it was the researchanddevelopment references.

Edited by WaveFunctionP
Link to comment
Share on other sites

I removed the rnd references, which apparently I didn't need to begin with. :P

Now it is recovering science properly now, and appears to be running experiments at the appropriate times.

private void TransferScience()
{
var v = FlightGlobals.ActiveVessel;
var containerList = v.FindPartModulesImplementing<ModuleScienceContainer>();
if (container == null) container = containerList[0];

var currentBody = v.mainBody;
var currentBiome = ScienceUtil.GetExperimentBiome(currentBody, v.latitude, v.longitude);
var currentSituation = ScienceUtil.GetExperimentSituation(v);

var experimentList = v.FindPartModulesImplementing<ModuleScienceExperiment>();
foreach (ModuleScienceExperiment experiment in experimentList)
{
var dataIsInContainer = false;
foreach (ScienceData data in container.GetData())
{
var currentExperiment = (experiment.experimentID + "@" + currentBody.name + currentSituation + currentBiome);
if (v.landedAt != "")
{
currentExperiment = (experiment.experimentID + "@" + currentBody.name + currentSituation + v.landedAt);
}
if (data.subjectID == currentExperiment)
{
Debug.Log("experiment: " + currentExperiment);
Debug.Log("data:" + data.subjectID);
dataIsInContainer = true;
}
}
if (dataIsInContainer == false)
{
experiment.DeployExperiment();
}
}

container.StoreData(experimentList.Cast<IScienceDataContainer>().ToList(), true);
}

I'm still getting more than one experiment run, which pops up a duplicate dialog. Which is annoying. But at least it isn't breaking installs any longer. :)

Link to comment
Share on other sites

This thread is quite old. Please consider starting a new thread rather than reviving this one.

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...