Ejemplo n.º 1
0
        [KSPEvent(guiActive = false, guiActiveUnfocused = true, guiActiveUncommand = true, guiName = "#KERBALISM_HardDrive_TakeData", active = true, groupName = "Science", groupDisplayName = "#KERBALISM_Group_Science")]        //Science
        public void TakeData()
        {
            // disable for dead eva kerbals
            Vessel v = FlightGlobals.ActiveVessel;

            if (v == null || EVA.IsDead(v))
            {
                return;
            }

            // transfer data
            if (!Drive.Transfer(drive, v, PreferencesScience.Instance.sampleTransfer || Lib.CrewCount(v) > 0))
            {
                Message.Post
                (
                    Lib.Color(Lib.BuildString(Local.HardDrive_WARNING_title), Lib.Kolor.Red, true), //"WARNING: not evering copied"
                    Lib.BuildString(Local.HardDrive_WARNING)                                        //"Storage is at capacity"
                );
            }
        }
Ejemplo n.º 2
0
        void VesselDock(GameEvents.FromToAction <Part, Part> e)
        {
            // note:
            //  we do not forget vessel data here, it just became inactive
            //  and ready to be implicitly activated again on undocking
            //  we do however tweak the data of the vessel being docked a bit,
            //  to avoid states getting out of sync, leading to unintuitive behaviours
            VesselData vd = DB.Vessel(e.from.vessel);

            vd.msg_belt    = false;
            vd.msg_signal  = false;
            vd.storm_age   = 0.0;
            vd.storm_time  = 0.0;
            vd.storm_state = 0;
            vd.supplies.Clear();
            vd.scansat_id.Clear();

            // merge drives data
            Drive.Transfer(e.from.vessel, e.to.vessel);
        }
Ejemplo n.º 3
0
        public void StoreData()
        {
            // disable for dead eva kerbals
            Vessel v = FlightGlobals.ActiveVessel;

            if (v == null || EVA.IsDead(v))
            {
                return;
            }

            // transfer data
            if (!Drive.Transfer(v, drive, PreferencesScience.Instance.sampleTransfer || Lib.CrewCount(v) > 0))
            {
                Message.Post
                (
                    Lib.Color("red", Lib.BuildString("WARNING: not evering copied"), true),
                    Lib.BuildString("Storage is at capacity")
                );
            }
        }
Ejemplo n.º 4
0
        // return name of file being transmitted from vessel specified
        public static string Transmitting(Vessel v, bool linked)
        {
            // never transmitting if science system is disabled
            if (!Features.Science)
            {
                return(string.Empty);
            }

            // not transmitting if unlinked
            if (!linked)
            {
                return(string.Empty);
            }

            // not transmitting if there is no ec left
            if (!Lib.IsPowered(v))
            {
                return(string.Empty);
            }

            foreach (var p in Cache.WarpCache(v).files)
            {
                return(p.Key);
            }

            // get first file flagged for transmission, AND has a ts at least 5 seconds old or is > 0.001Mb in size
            foreach (var drive in Drive.GetDrives(v, true))
            {
                double now = Planetarium.GetUniversalTime();
                foreach (var p in drive.files)
                {
                    if (drive.GetFileSend(p.Key) && (p.Value.ts + 3 < now || p.Value.size > min_file_size))
                    {
                        return(p.Key);
                    }
                }
            }

            // no file flagged for transmission
            return(string.Empty);
        }
Ejemplo n.º 5
0
		void FromEVA(GameEvents.FromToAction<Part, Part> data)
		{
			// for each resource in the eva kerbal
			for (int i = 0; i < data.from.Resources.Count; ++i)
			{
				// get the resource
				PartResource res = data.from.Resources[i];

				// add leftovers to the vessel
				data.to.RequestResource(res.resourceName, -res.amount);
			}

			// merge drives data
			Drive.Transfer(data.from.vessel, data.to.vessel);

			// forget vessel data
			DB.vessels.Remove(Lib.RootID(data.from.vessel));

			// execute script
			DB.Vessel(data.to.vessel).computer.Execute(data.to.vessel, ScriptType.eva_in);
		}
Ejemplo n.º 6
0
        public void DumpData(ScienceData data)
        {
            // get drive
            Drive drive = DB.Vessel(vessel).drive;

            // if not the preferred drive
            if (drive.location != part.flightID)
            {
                return;
            }

            // remove the data
            if (data.baseTransmitValue > float.Epsilon || data.transmitBonus > double.Epsilon)
            {
                drive.delete_file(data.subjectID, data.dataAmount);
            }
            else
            {
                drive.delete_sample(data.subjectID, data.dataAmount);
            }
        }
Ejemplo n.º 7
0
 public VesselData()
 {
     msg_signal      = false;
     msg_belt        = false;
     cfg_ec          = true;
     cfg_supply      = true;
     cfg_signal      = true;
     cfg_malfunction = true;
     cfg_storm       = true;
     cfg_script      = true;
     cfg_highlights  = true;
     cfg_showlink    = true;
     storm_time      = 0.0;
     storm_age       = 0.0;
     storm_state     = 0;
     group           = "NONE";
     computer        = new Computer();
     drive           = new Drive();
     supplies        = new Dictionary <string, SupplyData>();
     scansat_id      = new List <uint>();
 }
Ejemplo n.º 8
0
        static void Render_file(Panel p, string filename, File file, Drive drive, bool short_strings, double rate)
        {
            // get experiment info
            ExperimentInfo exp = Science.Experiment(filename);

            // render experiment name
            string exp_label = Lib.BuildString
                               (
                "<b>",
                Lib.Ellipsis(exp.name, Styles.ScaleStringLength(short_strings ? 24 : 38)),
                "</b> <size=", Styles.ScaleInteger(10).ToString(), ">",
                Lib.Ellipsis(exp.situation, Styles.ScaleStringLength((short_strings ? 32 : 62) - Lib.Ellipsis(exp.name, Styles.ScaleStringLength(short_strings ? 24 : 38)).Length)),
                "</size>"
                               );
            string exp_tooltip = Lib.BuildString
                                 (
                exp.name, "\n",
                "<color=#aaaaaa>", exp.situation, "</color>"
                                 );
            double exp_value = Science.Value(filename, file.size);

            if (exp_value > double.Epsilon)
            {
                exp_tooltip = Lib.BuildString(exp_tooltip, "\n<b>", Lib.HumanReadableScience(exp_value), "</b>");
            }
            if (rate > 0)
            {
                exp_tooltip = Lib.BuildString(exp_tooltip, "\n<i>" + Lib.HumanReadableDuration(file.size / rate) + "</i>");
            }
            p.AddContent(exp_label, Lib.HumanReadableDataSize(file.size), exp_tooltip);

            p.AddIcon(file.send ? Icons.send_cyan : Icons.send_black, "Flag the file for transmission to <b>DSN</b>", () => { file.send = !file.send; });
            p.AddIcon(Icons.toggle_red, "Delete the file", () => Lib.Popup
                      (
                          "Warning!",
                          Lib.BuildString("Do you really want to delete ", exp.fullname, "?"),
                          new DialogGUIButton("Delete it", () => drive.files.Remove(filename)),
                          new DialogGUIButton("Keep it", () => { })
                      ));
        }
Ejemplo n.º 9
0
        static void Render_sample(Panel p, string filename, Sample sample, Drive drive, bool short_strings)
        {
            // get experiment info
            ExperimentInfo exp = Science.Experiment(filename);

            // render experiment name
            string exp_label = Lib.BuildString
                               (
                "<b>",
                Lib.Ellipsis(exp.name, Styles.ScaleStringLength(short_strings ? 24 : 38)),
                "</b> <size=", Styles.ScaleInteger(10).ToString(), ">",
                Lib.Ellipsis(exp.situation, Styles.ScaleStringLength((short_strings ? 32 : 62) - Lib.Ellipsis(exp.name, Styles.ScaleStringLength(short_strings ? 24 : 38)).Length)),
                "</size>"
                               );
            string exp_tooltip = Lib.BuildString
                                 (
                exp.name, "\n",
                "<color=#aaaaaa>", exp.situation, "</color>"
                                 );
            double exp_value = Science.Value(filename, sample.size);

            if (exp_value > double.Epsilon)
            {
                exp_tooltip = Lib.BuildString(exp_tooltip, "\n<b>", Lib.HumanReadableScience(exp_value), "</b>");
            }
            if (sample.mass > Double.Epsilon)
            {
                exp_tooltip = Lib.BuildString(exp_tooltip, "\n<b>", Lib.HumanReadableMass(sample.mass), "</b>");
            }

            p.AddContent(exp_label, Lib.HumanReadableSampleSize(sample.size), exp_tooltip);
            p.AddIcon(sample.analyze ? Icons.lab_cyan : Icons.lab_black, "Flag the file for analysis in a <b>laboratory</b>", () => { sample.analyze = !sample.analyze; });
            p.AddIcon(Icons.toggle_red, "Dump the sample", () => Lib.Popup
                      (
                          "Warning!",
                          Lib.BuildString("Do you really want to dump ", exp.fullname, "?"),
                          new DialogGUIButton("Dump it", () => drive.samples.Remove(filename)),
                          new DialogGUIButton("Keep it", () => { })
                      ));
        }
Ejemplo n.º 10
0
        public override void OnStart(StartState state)
        {
            // don't break tutorial scenarios
            if (Lib.DisableScenario(this))
            {
                return;
            }

            if (drive == null)
            {
                if (Lib.IsEditor())
                {
                    drive = new Drive(title, dataCapacity, sampleCapacity);
                }
                else
                {
                    drive = DB.Vessel(vessel).DriveForPart(title, part, dataCapacity, sampleCapacity);
                }
            }

            UpdateCapacity();
        }
Ejemplo n.º 11
0
        void VesselDestroyed(Vessel v)
        {
            // rescan the damn kerbals
            // - vessel crew is empty at destruction time
            // - we can't even use the flightglobal roster, because sometimes it isn't updated yet at this point
            HashSet <string> kerbals_alive = new HashSet <string>();
            HashSet <string> kerbals_dead  = new HashSet <string>();

            foreach (Vessel ov in FlightGlobals.Vessels)
            {
                foreach (ProtoCrewMember c in Lib.CrewList(ov))
                {
                    kerbals_alive.Add(c.name);
                }
            }
            foreach (string key in DB.Kerbals().Keys)
            {
                if (!kerbals_alive.Contains(key))
                {
                    kerbals_dead.Add(key);
                }
            }
            foreach (string n in kerbals_dead)
            {
                // we don't know if the kerbal really is dead, or if it is just not currently assigned to a mission
                DB.KillKerbal(n, false);
            }

            // purge the caches
            ResourceCache.Purge(v);                 // works with loaded and unloaded vessels
            Cache.PurgeVesselCaches(v);             // works with loaded and unloaded vessels

            // delete data on unloaded vessels only (this is handled trough OnPartWillDie for loaded vessels)
            if (!v.loaded)
            {
                Drive.DeleteDrivesData(v);
            }
        }
Ejemplo n.º 12
0
        internal static double RecordData(ScienceData data, MetaData meta)
        {
            double remaining = data.dataAmount;

            foreach (var drive in Drive.GetDrives(meta.vessel.KerbalismData(), false))
            {
                var size = Math.Min(remaining, drive.FileCapacityAvailable());
                if (size > 0)
                {
                    drive.Record_file(meta.subjectData, size, true, true);
                    remaining -= size;
                }
            }

            if (remaining > 0)
            {
                Message.Post(
                    Lib.Color(Lib.BuildString(meta.subjectData.FullTitle, " stored partially"), Lib.Kolor.Orange),
                    "Not enough space on hard drive"
                    );
            }
            return(remaining);
        }
Ejemplo n.º 13
0
        // transfer data between two vessels
        public static void Transfer(Vessel src, Vessel dst)
        {
            // get drives
            Drive a = DB.Vessel(src).drive;
            Drive b = DB.Vessel(dst).drive;

            // get size of data being transfered
            double amount = a.Size();

            // if there is data
            if (amount > double.Epsilon)
            {
                // transfer the data
                a.Move(b);

                // inform the user
                Message.Post
                (
                    Lib.BuildString(Lib.HumanReadableDataSize(amount), " ", Localizer.Format("#KERBALISM_Science_ofdatatransfer")),
                    Lib.BuildString(Localizer.Format("#KERBALISM_Generic_FROM"), " <b>", src.vesselName, "</b> ", Localizer.Format("#KERBALISM_Generic_TO"), " <b>", dst.vesselName, "</b>")
                );
            }
        }
Ejemplo n.º 14
0
        // get sample to analyze, return null if there isn't a sample
        static string next_sample(Vessel v)
        {
            // get vessel drive
            Drive drive = DB.Vessel(v).drive;

            // for each sample
            foreach (var pair in drive.samples)
            {
                // shortcuts
                string filename = pair.Key;
                Sample sample   = pair.Value;

                // if flagged for analysis
                if (sample.analyze)
                {
                    // we found it
                    return(filename);
                }
            }

            // there was no sample to analyze
            return(string.Empty);
        }
Ejemplo n.º 15
0
 public VesselData()
 {
     msg_signal      = false;
     msg_belt        = false;
     cfg_ec          = PreferencesMessages.Instance.ec;
     cfg_supply      = PreferencesMessages.Instance.supply;
     cfg_signal      = PreferencesMessages.Instance.signal;
     cfg_malfunction = PreferencesMessages.Instance.malfunction;
     cfg_storm       = PreferencesMessages.Instance.storm;
     cfg_script      = PreferencesMessages.Instance.script;
     cfg_highlights  = PreferencesBasic.Instance.highlights;
     cfg_showlink    = true;
     hyspos_signal   = 0.0;
     hysneg_signal   = 5.0;
     storm_time      = 0.0;
     storm_age       = 0.0;
     storm_state     = 0;
     group           = "NONE";
     computer        = new Computer();
     drive           = new Drive();
     supplies        = new Dictionary <string, SupplyData>();
     scansat_id      = new List <uint>();
 }
Ejemplo n.º 16
0
        // transfer data between two vessels
        public static void transfer(Vessel src, Vessel dst)
        {
            // get drives
            Drive a = DB.Vessel(src).drive;
            Drive b = DB.Vessel(dst).drive;

            // get size of data being transfered
            double amount = a.size();

            // if there is data
            if (amount > double.Epsilon)
            {
                // transfer the data
                a.move(b);

                // inform the user
                Message.Post
                (
                    Lib.BuildString(Lib.HumanReadableDataSize(amount), " of data transfered"),
                    Lib.BuildString("from <b>", src.vesselName, "</b> to <b>", dst.vesselName, "</b>")
                );
            }
        }
Ejemplo n.º 17
0
		void VesselModified(Vessel vessel_a)
		{
			// do nothing in the editor
			if (Lib.IsEditor()) return;

			// bah
			if (string.IsNullOrEmpty(vessel_a.vesselName)) return;

			// get drive from first vessel
			// - there is a possibility this will create it
			// - we avoid adding a db entry for invalid vessels
			Drive drive_a = Cache.VesselInfo(vessel_a).is_valid ? DB.Vessel(vessel_a).drive : new Drive();

			// for each loaded vessel
			foreach (Vessel vessel_b in FlightGlobals.VesselsLoaded)
			{
				// do not check against itself
				if (vessel_a.id == vessel_b.id) continue;

				// get drive of the other vessel
				// - there is a possibility this will create it
				// - we avoid adding a db entry for invalid vessels
				Drive drive_b = Cache.VesselInfo(vessel_b).is_valid ? DB.Vessel(vessel_b).drive : new Drive();

				// if location of A is now in B, or viceversa
				// - this support the case when one or both the drives locations are 0
				if (vessel_a.parts.Find(k => k.flightID == drive_b.location) != null
				 || vessel_b.parts.Find(k => k.flightID == drive_a.location) != null)
				{
					// swap the drives
					Lib.Swap(ref DB.Vessel(vessel_a).drive, ref DB.Vessel(vessel_b).drive);

					// done, no need to go through the rest of the loaded vessels
					break;
				}
			}
		}
Ejemplo n.º 18
0
        public Drive BestDrive(double minDataCapacity = 0, int minSlots = 0)
        {
            Drive result = null;

            foreach (var drive in drives.Values)
            {
                if (result == null)
                {
                    result = drive;
                    continue;
                }

                if (minDataCapacity > double.Epsilon && drive.FileCapacityAvailable() < minDataCapacity)
                {
                    continue;
                }
                if (minSlots > 0 && drive.SampleCapacityAvailable() < minSlots)
                {
                    continue;
                }

                if (minDataCapacity > double.Epsilon && drive.FileCapacityAvailable() > result.FileCapacityAvailable())
                {
                    result = drive;
                }
                if (minSlots > 0 && drive.SampleCapacityAvailable() > result.SampleCapacityAvailable())
                {
                    result = drive;
                }
            }
            if (result == null)
            {
                // vessel has no drive.
                return(new Drive("Broken", 0, 0));
            }
            return(result);
        }
Ejemplo n.º 19
0
        void VesselDestroyed(Vessel v)
        {
            DB.vessels.Remove(Lib.VesselID(v));

            // rescan the damn kerbals
            // - vessel crew is empty at destruction time
            // - we can't even use the flightglobal roster, because sometimes it isn't updated yet at this point
            HashSet <string> kerbals_alive = new HashSet <string>();
            HashSet <string> kerbals_dead  = new HashSet <string>();

            foreach (Vessel ov in FlightGlobals.Vessels)
            {
                foreach (ProtoCrewMember c in Lib.CrewList(ov))
                {
                    kerbals_alive.Add(c.name);
                }
            }
            foreach (KeyValuePair <string, KerbalData> p in DB.Kerbals())
            {
                if (!kerbals_alive.Contains(p.Key))
                {
                    kerbals_dead.Add(p.Key);
                }
            }
            foreach (string n in kerbals_dead)
            {
                // we don't know if the kerbal really is dead, or if it is just not currently assigned to a mission
                DB.KillKerbal(n, false);
            }


            // purge the caches
            ResourceCache.Purge(v);
            Drive.Purge(v);
            Cache.PurgeObjects(v);
        }
Ejemplo n.º 20
0
        /// <summary> Get a drive for storing samples. Will return null if there are no drives on the vessel </summary>
        public static Drive SampleDrive(VesselData vesselData, double size = 0, SubjectData subject = null)
        {
            Drive result = null;

            foreach (var drive in GetDrives(vesselData))
            {
                if (result == null)
                {
                    result = drive;
                    continue;
                }

                double available = drive.SampleCapacityAvailable(subject);
                if (size > double.Epsilon && available < size)
                {
                    continue;
                }
                if (available > result.SampleCapacityAvailable(subject))
                {
                    result = drive;
                }
            }
            return(result);
        }
Ejemplo n.º 21
0
        static void Render_File(Panel p, string filename, File file, Drive drive)
        {
            // get experiment info
            ExperimentInfo exp = Science.Experiment(filename);

            // render experiment name
            string exp_label = Lib.BuildString
                               (
                "<b>",
                Lib.Ellipsis(exp.name, 24),
                "</b> <size=10>",
                Lib.Ellipsis(exp.situation, 32u - (uint)Math.Min(24, exp.name.Length)),
                "</size>"
                               );
            string exp_tooltip = Lib.BuildString
                                 (
                exp.name, "\n",
                "<color=#aaaaaa>", exp.situation, "</color>"
                                 );
            double exp_value = Science.Value(filename, file.size);

            if (exp_value > double.Epsilon)
            {
                exp_tooltip = Lib.BuildString(exp_tooltip, "\n<b>", Lib.HumanReadableScience(exp_value), "</b>");
            }

            p.SetContent(exp_label, Lib.HumanReadableDataSize(file.size), exp_tooltip);
            p.SetIcon(file.send ? Icons.send_cyan : Icons.send_black, "Flag the file for transmission to <b>DSN</b>", () => { file.send = !file.send; });
            p.SetIcon(Icons.toggle_red, "Delete the file", () => Lib.Popup
                      (
                          "Warning!",
                          Lib.BuildString("Do you really want to delete ", exp.fullname, "?"),
                          new DialogGUIButton("Delete it", () => drive.files.Remove(filename)),
                          new DialogGUIButton("Keep it", () => {})
                      ));
        }
Ejemplo n.º 22
0
        private static string TestForIssues(Vessel v, Resource_info ec, Experiment experiment, uint hdId, bool broken,
                                            double remainingSampleMass, bool didPrepare, bool isShrouded, string last_subject_id)
        {
            var subject_id = Science.Generate_subject_id(experiment.experiment_id, v);

            if (broken)
            {
                return("broken");
            }

            if (isShrouded && !experiment.allow_shrouded)
            {
                return("shrouded");
            }

            bool needsReset = experiment.crew_reset.Length > 0 &&
                              !string.IsNullOrEmpty(last_subject_id) && subject_id != last_subject_id;

            if (needsReset)
            {
                return("reset required");
            }

            if (ec.amount < double.Epsilon && experiment.ec_rate > double.Epsilon)
            {
                return("no Electricity");
            }

            if (!string.IsNullOrEmpty(experiment.crew_operate))
            {
                var cs = new CrewSpecs(experiment.crew_operate);
                if (!cs && Lib.CrewCount(v) > 0)
                {
                    return("crew on board");
                }
                else if (cs && !cs.Check(v))
                {
                    return(cs.Warning());
                }
            }

            if (!experiment.sample_collecting && remainingSampleMass < double.Epsilon && experiment.sample_mass > double.Epsilon)
            {
                return("depleted");
            }

            if (!didPrepare && !string.IsNullOrEmpty(experiment.crew_prepare))
            {
                return("not prepared");
            }

            string situationIssue = Science.TestRequirements(experiment.experiment_id, experiment.requires, v);

            if (situationIssue.Length > 0)
            {
                return(Science.RequirementText(situationIssue));
            }

            var    experimentSize = Science.Experiment(subject_id).max_amount;
            double chunkSize      = Math.Min(experiment.data_rate * Kerbalism.elapsed_s, experimentSize);
            Drive  drive          = GetDrive(experiment, v, hdId, chunkSize, subject_id);

            var    isFile    = experiment.sample_mass < double.Epsilon;
            double available = 0;

            if (isFile)
            {
                available  = drive.FileCapacityAvailable();
                available += Cache.WarpCache(v).FileCapacityAvailable();
            }
            else
            {
                available = drive.SampleCapacityAvailable(subject_id);
            }

            if (Math.Min(experiment.data_rate * Kerbalism.elapsed_s, experimentSize) > available)
            {
                return(insufficient_storage);
            }

            return(string.Empty);
        }
Ejemplo n.º 23
0
        private static bool DoRecord(Experiment experiment, string subject_id, Vessel vessel, Resource_info ec, uint hdId,
                                     Vessel_resources resources, List <KeyValuePair <string, double> > resourceDefs,
                                     double remainingSampleMass, double dataSampled,
                                     out double sampledOut, out double remainingSampleMassOut)
        {
            // default output values for early returns
            sampledOut             = dataSampled;
            remainingSampleMassOut = remainingSampleMass;

            var exp = Science.Experiment(subject_id);

            if (Done(exp, dataSampled))
            {
                return(true);
            }

            double elapsed   = Kerbalism.elapsed_s;
            double chunkSize = Math.Min(experiment.data_rate * elapsed, exp.max_amount);
            double massDelta = experiment.sample_mass * chunkSize / exp.max_amount;

            Drive drive = GetDrive(experiment, vessel, hdId, chunkSize, subject_id);

            // on high time warp this chunk size could be too big, but we could store a sizable amount if we process less
            bool   isFile      = experiment.sample_mass < float.Epsilon;
            double maxCapacity = isFile ? drive.FileCapacityAvailable() : drive.SampleCapacityAvailable(subject_id);

            Drive warpCacheDrive = null;

            if (isFile)
            {
                if (drive.GetFileSend(subject_id))
                {
                    warpCacheDrive = Cache.WarpCache(vessel);
                }
                if (warpCacheDrive != null)
                {
                    maxCapacity += warpCacheDrive.FileCapacityAvailable();
                }
            }

            double factor = Rate(vessel, chunkSize, maxCapacity, elapsed, ec, experiment.ec_rate, resources, resourceDefs);

            if (factor < double.Epsilon)
            {
                return(false);
            }

            chunkSize *= factor;
            massDelta *= factor;
            elapsed   *= factor;

            bool stored = false;

            if (chunkSize > double.Epsilon)
            {
                if (isFile)
                {
                    if (warpCacheDrive != null)
                    {
                        double s = Math.Min(chunkSize, warpCacheDrive.FileCapacityAvailable());
                        stored = warpCacheDrive.Record_file(subject_id, s, true);

                        if (chunkSize > s)                        // only write to persisted drive if the data cannot be transmitted in this tick
                        {
                            stored &= drive.Record_file(subject_id, chunkSize - s, true);
                        }
                    }
                    else
                    {
                        stored = drive.Record_file(subject_id, chunkSize, true);
                    }
                }
                else
                {
                    stored = drive.Record_sample(subject_id, chunkSize, massDelta);
                }
            }

            if (!stored)
            {
                return(false);
            }

            // consume resources
            ec.Consume(experiment.ec_rate * elapsed, "experiment");
            foreach (var p in resourceDefs)
            {
                resources.Consume(vessel, p.Key, p.Value * elapsed, "experiment");
            }

            dataSampled += chunkSize;
            dataSampled  = Math.Min(dataSampled, exp.max_amount);
            sampledOut   = dataSampled;
            if (!experiment.sample_collecting)
            {
                remainingSampleMass -= massDelta;
                remainingSampleMass  = Math.Max(remainingSampleMass, 0);
            }
            remainingSampleMassOut = remainingSampleMass;
            return(true);
        }
Ejemplo n.º 24
0
        /// <summary>
        /// If short_strings parameter is true then the strings used for display of the data will be shorter when inflight.
        /// </summary>
        public static void Fileman(this Panel p, Vessel v, bool short_strings = false)
        {
            // avoid corner-case when this is called in a lambda after scene changes
            v = FlightGlobals.FindVessel(v.id);

            // if vessel doesn't exist anymore, leave the panel empty
            if (v == null)
            {
                return;
            }

            // get info from the cache
            Vessel_info vi = Cache.VesselInfo(v);

            // if not a valid vessel, leave the panel empty
            if (!vi.is_valid)
            {
                return;
            }

            // set metadata
            p.Title(Lib.BuildString(Lib.Ellipsis(v.vesselName, Styles.ScaleStringLength(40)), " <color=#cccccc>FILE MANAGER</color>"));
            p.Width(Styles.ScaleWidthFloat(465.0f));
            p.paneltype = Panel.PanelType.data;

            // time-out simulation
            if (!Lib.IsControlUnit(v) && p.Timeout(vi))
            {
                return;
            }

            var drives = Drive.GetDriveParts(v);

            int    filesCount        = 0;
            double usedDataCapacity  = 0;
            double totalDataCapacity = 0;

            int    samplesCount     = 0;
            int    usedSlots        = 0;
            int    totalSlots       = 0;
            double totalMass        = 0;
            bool   unlimitedData    = false;
            bool   unlimitedSamples = false;

            foreach (var idDrivePair in drives)
            {
                var drive = idDrivePair.Value;

                if (!drive.is_private)
                {
                    usedDataCapacity  += drive.FilesSize();
                    totalDataCapacity += drive.dataCapacity;

                    unlimitedData    |= drive.dataCapacity < 0;
                    unlimitedSamples |= drive.sampleCapacity < 0;

                    usedSlots  += drive.SamplesSize();
                    totalSlots += drive.sampleCapacity;
                }

                filesCount   += drive.files.Count;
                samplesCount += drive.samples.Count;
                foreach (var sample in drive.samples.Values)
                {
                    totalMass += sample.mass;
                }
            }

            if (filesCount > 0 || totalDataCapacity > 0)
            {
                var title = "DATA " + Lib.HumanReadableDataSize(usedDataCapacity);
                if (!unlimitedData)
                {
                    title += Lib.BuildString(" (", Lib.HumanReadablePerc((totalDataCapacity - usedDataCapacity) / totalDataCapacity), " available)");
                }
                p.AddSection(title);

                foreach (var idDrivePair in drives)
                {
                    uint partId = idDrivePair.Key;
                    var  drive  = idDrivePair.Value;
                    foreach (var pair in drive.files)
                    {
                        string filename = pair.Key;
                        File   file     = pair.Value;
                        Render_file(p, partId, filename, file, drive, short_strings && Lib.IsFlight(), v);
                    }
                }

                if (filesCount == 0)
                {
                    p.AddContent("<i>no files</i>", string.Empty);
                }
            }

            if (samplesCount > 0 || totalSlots > 0)
            {
                var title = "SAMPLES " + Lib.HumanReadableMass(totalMass) + " " + Lib.HumanReadableSampleSize(usedSlots);
                if (totalSlots > 0 && !unlimitedSamples)
                {
                    title += ", " + Lib.HumanReadableSampleSize(totalSlots) + " available";
                }
                p.AddSection(title);

                foreach (var idDrivePair in drives)
                {
                    uint partId = idDrivePair.Key;
                    var  drive  = idDrivePair.Value;
                    foreach (var pair in drive.samples)
                    {
                        string samplename = pair.Key;
                        Sample sample     = pair.Value;
                        Render_sample(p, partId, samplename, sample, drive, short_strings && Lib.IsFlight());
                    }
                }

                if (samplesCount == 0)
                {
                    p.AddContent("<i>no samples</i>", string.Empty);
                }
            }
        }
Ejemplo n.º 25
0
        void VesselRecoveryProcessing(ProtoVessel v, MissionRecoveryDialog dialog, float score)
        {
            // note:
            // this function accumulate science stored in drives on recovery,
            // and visualize the data in the recovery dialog window

            // do nothing if science system is disabled, or in sandbox mode
            if (!Features.Science || HighLogic.CurrentGame.Mode == Game.Modes.SANDBOX)
            {
                return;
            }

            var vesselID = Lib.VesselID(v);

            // get the drive data from DB
            if (!DB.vessels.ContainsKey(vesselID))
            {
                return;
            }

            foreach (Drive drive in Drive.GetDrives(v))
            {
                // for each file in the drive
                foreach (KeyValuePair <string, File> p in drive.files)
                {
                    // shortcuts
                    string filename = p.Key;
                    File   file     = p.Value;

                    // de-buffer partially transmitted data
                    file.size += file.buff;
                    file.buff  = 0.0;

                    // get subject
                    ScienceSubject subject = ResearchAndDevelopment.GetSubjectByID(filename);

                    // credit science
                    float credits = Science.Credit(filename, file.size, false, v);

                    // create science widged
                    ScienceSubjectWidget widged = ScienceSubjectWidget.Create
                                                  (
                        subject,                              // subject
                        (float)file.size,                     // data gathered
                        credits,                              // science points
                        dialog                                // recovery dialog
                                                  );

                    // add widget to dialog
                    dialog.AddDataWidget(widged);

                    // add science credits to total
                    dialog.scienceEarned += (float)credits;
                }

                // for each sample in the drive
                // for each file in the drive
                foreach (KeyValuePair <string, Sample> p in drive.samples)
                {
                    // shortcuts
                    string filename = p.Key;
                    Sample sample   = p.Value;

                    // get subject
                    ScienceSubject subject = ResearchAndDevelopment.GetSubjectByID(filename);

                    // credit science
                    float credits = Science.Credit(filename, sample.size, false, v);

                    // create science widged
                    ScienceSubjectWidget widged = ScienceSubjectWidget.Create
                                                  (
                        subject,                              // subject
                        (float)sample.size,                   // data gathered
                        credits,                              // science points
                        dialog                                // recovery dialog
                                                  );

                    // add widget to dialog
                    dialog.AddDataWidget(widged);

                    // add science credits to total
                    dialog.scienceEarned += (float)credits;
                }
            }
        }
Ejemplo n.º 26
0
        // consume EC for transmission, and transmit science data
        public static void Update(Vessel v, VesselData vd, ResourceInfo ec, double elapsed_s)
        {
            // do nothing if science system is disabled
            if (!Features.Science)
            {
                return;
            }

            // consume ec for transmitters
            ec.Consume(vd.Connection.ec_idle * elapsed_s, ResourceBroker.CommsIdle);

            // avoid corner-case when RnD isn't live during scene changes
            // - this avoid losing science if the buffer reach threshold during a scene change
            if (HighLogic.CurrentGame.Mode != Game.Modes.SANDBOX && ResearchAndDevelopment.Instance == null)
            {
                return;
            }

            // clear list of files transmitted
            vd.filesTransmitted.Clear();

            // check connection
            if (vd.Connection == null ||
                !vd.Connection.linked ||
                vd.Connection.rate <= 0.0 ||
                !vd.deviceTransmit ||
                ec.Amount < vd.Connection.ec_idle * elapsed_s)
            {
                // reset all files transmit rate
                foreach (Drive drive in Drive.GetDrives(vd, true))
                {
                    foreach (File f in drive.files.Values)
                    {
                        f.transmitRate = 0.0;
                    }
                }

                // do nothing else
                return;
            }

            double totalTransmitCapacity     = vd.Connection.rate * elapsed_s;
            double remainingTransmitCapacity = totalTransmitCapacity;

            GetFilesToTransmit(v, vd);

            if (xmitFiles.Count == 0)
            {
                return;
            }

            UnityEngine.Profiling.Profiler.BeginSample("Kerbalism.Science.Update-Loop");

            // traverse the list in reverse because :
            // - warp cache files are at the end, and they are always transmitted regerdless of transmit capacity
            // - others files are next, sorted in science value per MB ascending order
            for (int i = xmitFiles.Count - 1; i >= 0; i--)
            {
                XmitFile xmitFile = xmitFiles[i];

                if (xmitFile.file.size == 0.0)
                {
                    continue;
                }

                // always transmit everything in the warp cache
                if (!xmitFile.isInWarpCache && remainingTransmitCapacity <= 0.0)
                {
                    break;
                }

                // determine how much data is transmitted
                double transmitted = xmitFile.isInWarpCache ? xmitFile.file.size : Math.Min(xmitFile.file.size, remainingTransmitCapacity);

                if (transmitted == 0.0)
                {
                    continue;
                }

                // consume transmit capacity
                remainingTransmitCapacity -= transmitted;

                // get science value
                double xmitScienceValue = transmitted * xmitFile.sciencePerMB;

                // consume data in the file
                xmitFile.file.size -= transmitted;

                // remove science collected (ignoring final science value clamped to subject completion)
                xmitFile.file.subjectData.RemoveScienceCollectedInFlight(xmitScienceValue);

                // save transmit rate for the file, and add it to the VesselData list of files being transmitted
                if (xmitFile.isInWarpCache && xmitFile.realDriveFile != null)
                {
                    xmitFile.realDriveFile.transmitRate = transmitted / elapsed_s;
                    vd.filesTransmitted.Add(xmitFile.realDriveFile);
                }
                else
                {
                    xmitFile.file.transmitRate = transmitted / elapsed_s;
                    vd.filesTransmitted.Add(xmitFile.file);
                }

                if (xmitScienceValue > 0.0)
                {
                    // add science to the subject (and eventually included subjects), trigger completion events, credit the science, return how much has been credited.
                    vd.scienceTransmitted += xmitFile.file.subjectData.RetrieveScience(xmitScienceValue, true, v.protoVessel, xmitFile.file);
                }
            }

            UnityEngine.Profiling.Profiler.EndSample();

            // consume EC cost for transmission (ec_idle is consumed above)
            double transmittedCapacity = totalTransmitCapacity - remainingTransmitCapacity;
            double transmissionCost    = (vd.Connection.ec - vd.Connection.ec_idle) * (transmittedCapacity / (vd.Connection.rate * elapsed_s));

            ec.Consume(transmissionCost * elapsed_s, ResourceBroker.CommsXmit);
        }
Ejemplo n.º 27
0
        private static void GetFilesToTransmit(Vessel v, VesselData vd)
        {
            UnityEngine.Profiling.Profiler.BeginSample("Kerbalism.Science.GetFilesToTransmit");
            Drive warpCache = Cache.WarpCache(v);

            xmitFiles.Clear();
            List <SubjectData> filesToRemove = new List <SubjectData>();

            foreach (Drive drive in Drive.GetDrives(vd, true))
            {
                foreach (File f in drive.files.Values)
                {
                    // always reset transmit rate
                    f.transmitRate = 0.0;

                    // delete empty files that aren't being transmitted
                    // note : this won't work in case the same subject is split over multiple files (on different drives)
                    if (f.size <= 0.0 && (!warpCache.files.ContainsKey(f.subjectData) || warpCache.files[f.subjectData].size <= 0.0))
                    {
                        filesToRemove.Add(f.subjectData);
                        continue;
                    }

                    // get files tagged for transmit
                    if (drive.GetFileSend(f.subjectData.Id))
                    {
                        xmitFiles.Add(new XmitFile(f, drive, f.subjectData.SciencePerMB, false));
                    }
                }

                // delete found empty files from the drive
                foreach (SubjectData fileToRemove in filesToRemove)
                {
                    drive.files.Remove(fileToRemove);
                }

                filesToRemove.Clear();
            }

            // sort files by science value per MB ascending order so high value files are transmitted first
            // because XmitFile list is processed from end to start
            UnityEngine.Profiling.Profiler.BeginSample("Kerbalism.Science.GetFilesToTransmit-Sort");
            xmitFiles.Sort((x, y) => x.sciencePerMB.CompareTo(y.sciencePerMB));
            UnityEngine.Profiling.Profiler.EndSample();

            // add all warpcache files to the beginning of the XmitFile list
            foreach (File f in warpCache.files.Values)
            {
                // don't transmit empty files
                if (f.size <= 0.0)
                {
                    continue;
                }

                // find the file on a "real" drive that correspond to this warpcache file
                // this allow to use the real file for displaying transmit info and saving state (filemanager, monitor, vesseldata...)
                int driveFileIndex = xmitFiles.FindIndex(df => df.file.subjectData == f.subjectData);
                if (driveFileIndex >= 0)
                {
                    xmitFiles.Add(new XmitFile(f, warpCache, f.subjectData.SciencePerMB, true, xmitFiles[driveFileIndex].file));
                }
                else
                {
                    xmitFiles.Add(new XmitFile(f, warpCache, f.subjectData.SciencePerMB, true));                     // should not be happening, but better safe than sorry
                }
            }
            UnityEngine.Profiling.Profiler.EndSample();
        }
Ejemplo n.º 28
0
        public void FixedUpdate()
        {
            if (scanner == null)
            {
                return;
            }
            if (!Features.Science)
            {
                return;
            }

            IsScanning = SCANsat.IsScanning(scanner);
            double new_coverage = SCANsat.Coverage(sensorType, vessel.mainBody);

            if (body_name == vessel.mainBody.name && new_coverage < body_coverage)
            {
                // SCANsat sometimes reports a coverage of 0, which is wrong
                new_coverage = body_coverage;
            }

            if (vessel.mainBody.name != body_name)
            {
                body_name     = vessel.mainBody.name;
                body_coverage = new_coverage;
            }
            else
            {
                double coverage_delta = new_coverage - body_coverage;
                body_coverage = new_coverage;
                var vd = DB.Vessel(vessel);

                if (IsScanning)
                {
                    Science.Generate_subject(experimentType, vessel);
                    var    subject_id = Science.Generate_subject_id(experimentType, vessel);
                    var    exp        = Science.Experiment(subject_id);
                    double size       = exp.max_amount * coverage_delta / 100.0;               // coverage is 0-100%
                    size += warp_buffer;

                    size = Drive.StoreFile(vessel, subject_id, size);
                    if (size > double.Epsilon)
                    {
                        // we filled all drives up to the brim but were unable to store everything
                        if (warp_buffer < double.Epsilon)
                        {
                            // warp buffer is empty, so lets store the rest there
                            warp_buffer = size;
                            size        = 0;
                        }
                        else
                        {
                            // warp buffer not empty. that's ok if we didn't get new data
                            if (coverage_delta < double.Epsilon)
                            {
                                size = 0;
                            }
                            // else we're scanning too fast. stop.
                        }

                        // cancel scanning and annoy the user
                        if (size > double.Epsilon)
                        {
                            warp_buffer = 0;
                            StopScan();
                            vd.scansat_id.Add(part.flightID);
                            Message.Post(Lib.Color("red", "Scanner halted", true), "Scanner halted on <b>" + vessel.vesselName + "</b>. No storage left on vessel.");
                        }
                    }
                }
                else if (vd.scansat_id.Contains(part.flightID))
                {
                    var vi = Cache.VesselInfo(vessel);
                    if (vi.free_capacity / vi.total_capacity > 0.9)                    // restart when 90% of capacity is available
                    {
                        StartScan();
                        vd.scansat_id.Remove(part.flightID);
                        if (vd.cfg_ec)
                        {
                            Message.Post(Lib.BuildString("SCANsat sensor resumed operations on <b>", vessel.vesselName, "</b>"));
                        }
                    }
                }
            }
        }
Ejemplo n.º 29
0
        // consume EC for transmission, and transmit science data
        public static void Update(Vessel v, Vessel_info vi, VesselData vd, Vessel_resources resources, double elapsed_s)
        {
            // do nothing if science system is disabled
            if (!Features.Science)
            {
                return;
            }

            // avoid corner-case when RnD isn't live during scene changes
            // - this avoid losing science if the buffer reach threshold during a scene change
            if (HighLogic.CurrentGame.Mode != Game.Modes.SANDBOX && ResearchAndDevelopment.Instance == null)
            {
                return;
            }

            // get connection info
            ConnectionInfo conn = vi.connection;

            if (conn == null)
            {
                return;
            }
            if (String.IsNullOrEmpty(vi.transmitting))
            {
                return;
            }

            Drive warpCache   = Cache.WarpCache(v);
            bool  isWarpCache = false;

            double transmitSize = conn.rate * elapsed_s;

            while (warpCache.files.Count > 0 ||            // transmit EVERYTHING in the cache, regardless of transmitSize.
                   (transmitSize > double.Epsilon && !String.IsNullOrEmpty(vi.transmitting)))
            {
                // get filename of data being downloaded
                var exp_filename = vi.transmitting;
                if (string.IsNullOrEmpty(exp_filename))
                {
                    break;
                }

                Drive drive = null;
                if (warpCache.files.ContainsKey(exp_filename))
                {
                    drive       = warpCache;
                    isWarpCache = true;
                }
                else
                {
                    drive       = FindDrive(v, exp_filename);
                    isWarpCache = false;
                }

                if (drive == null)
                {
                    break;
                }

                File file = drive.files[exp_filename];

                if (isWarpCache)
                {
                    file.buff     = file.size;
                    file.size     = 0;
                    transmitSize -= file.size;
                }
                else
                {
                    if (transmitSize < double.Epsilon)
                    {
                        break;
                    }

                    // determine how much data is transmitted
                    double transmitted = Math.Min(file.size, transmitSize);
                    transmitSize -= transmitted;

                    // consume data in the file
                    file.size -= transmitted;

                    // accumulate in the buffer
                    file.buff += transmitted;
                }

                // special case: file size on drive = 0 -> buffer is 0, so no need to do anyhting. just delete.
                if (file.buff > double.Epsilon)
                {
                    // this is the science value remaining for this experiment
                    var remainingValue = Value(exp_filename, 0);

                    // this is the science value of this sample
                    double dataValue = Value(exp_filename, file.buff);
                    bool   doCredit  = file.size <= double.Epsilon || dataValue > buffer_science_value;;

                    // if buffer science value is high enough or file was transmitted completely
                    if (doCredit)
                    {
                        var totalValue = TotalValue(exp_filename);

                        // collect the science data
                        Credit(exp_filename, file.buff, true, v.protoVessel);

                        // reset the buffer
                        file.buff = 0.0;

                        // this was the last useful bit, there is no more value in the experiment
                        if (remainingValue >= 0.1 && remainingValue - dataValue < 0.1)
                        {
                            Message.Post(
                                Lib.BuildString(Lib.HumanReadableScience(totalValue), " ", Experiment(exp_filename).FullName(exp_filename), " completed"),
                                Lib.TextVariant(
                                    "Our researchers will jump on it right now",
                                    "This cause some excitement",
                                    "These results are causing a brouhaha in R&D",
                                    "Our scientists look very confused",
                                    "The scientists won't believe these readings"
                                    ));
                        }
                    }
                }

                // if file was transmitted completely
                if (file.size <= double.Epsilon)
                {
                    // remove the file
                    drive.files.Remove(exp_filename);
                    vi.transmitting = Science.Transmitting(v, true);
                }
            }
        }
Ejemplo n.º 30
0
        public static void BackgroundUpdate(Vessel vessel, ProtoPartSnapshot p, ProtoPartModuleSnapshot m, KerbalismScansat kerbalismScansat,
                                            Part part_prefab, VesselData vd, Resource_info ec, double elapsed_s)
        {
            List <ProtoPartModuleSnapshot> scanners = Cache.VesselObjectsCache <List <ProtoPartModuleSnapshot> >(vessel, "scansat_" + p.flightID);

            if (scanners == null)
            {
                scanners = Lib.FindModules(p, "SCANsat");
                if (scanners.Count == 0)
                {
                    scanners = Lib.FindModules(p, "ModuleSCANresourceScanner");
                }
                Cache.SetVesselObjectsCache(vessel, "scansat_" + p.flightID, scanners);
            }

            if (scanners.Count == 0)
            {
                return;
            }
            var scanner = scanners[0];

            bool is_scanning = Lib.Proto.GetBool(scanner, "scanning");

            if (is_scanning && kerbalismScansat.ec_rate > double.Epsilon)
            {
                ec.Consume(kerbalismScansat.ec_rate * elapsed_s, "scanner");
            }

            if (!Features.Science)
            {
                if (is_scanning && ec.amount < double.Epsilon)
                {
                    SCANsat.StopScanner(vessel, scanner, part_prefab);
                    is_scanning = false;

                    // remember disabled scanner
                    vd.scansat_id.Add(p.flightID);

                    // give the user some feedback
                    if (vd.cfg_ec)
                    {
                        Message.Post(Lib.BuildString("SCANsat sensor was disabled on <b>", vessel.vesselName, "</b>"));
                    }
                }
                else if (vd.scansat_id.Contains(p.flightID))
                {
                    // if there is enough ec
                    // note: comparing against amount in previous simulation step
                    // re-enable at 25% EC
                    if (ec.level > 0.25)
                    {
                        // re-enable the scanner
                        SCANsat.ResumeScanner(vessel, m, part_prefab);
                        is_scanning = true;

                        // give the user some feedback
                        if (vd.cfg_ec)
                        {
                            Message.Post(Lib.BuildString("SCANsat sensor resumed operations on <b>", vessel.vesselName, "</b>"));
                        }
                    }
                }

                // forget active scanners
                if (is_scanning)
                {
                    vd.scansat_id.Remove(p.flightID);
                }

                return;
            }             // if(!Feature.Science)

            string body_name     = Lib.Proto.GetString(m, "body_name");
            int    sensorType    = (int)Lib.Proto.GetUInt(m, "sensorType");
            double body_coverage = Lib.Proto.GetDouble(m, "body_coverage");
            double warp_buffer   = Lib.Proto.GetDouble(m, "warp_buffer");

            double new_coverage = SCANsat.Coverage(sensorType, vessel.mainBody);

            if (body_name == vessel.mainBody.name && new_coverage < body_coverage)
            {
                // SCANsat sometimes reports a coverage of 0, which is wrong
                new_coverage = body_coverage;
            }

            if (vessel.mainBody.name != body_name)
            {
                body_name     = vessel.mainBody.name;
                body_coverage = new_coverage;
            }
            else
            {
                double coverage_delta = new_coverage - body_coverage;
                body_coverage = new_coverage;

                if (is_scanning)
                {
                    Science.Generate_subject(kerbalismScansat.experimentType, vessel);
                    var    subject_id = Science.Generate_subject_id(kerbalismScansat.experimentType, vessel);
                    var    exp        = Science.Experiment(subject_id);
                    double size       = exp.max_amount * coverage_delta / 100.0;               // coverage is 0-100%
                    size += warp_buffer;

                    if (size > double.Epsilon)
                    {
                        // store what we can
                        foreach (var d in Drive.GetDrives(vessel))
                        {
                            var available = d.FileCapacityAvailable();
                            var chunk     = Math.Min(size, available);
                            if (!d.Record_file(subject_id, chunk, true))
                            {
                                break;
                            }
                            size -= chunk;

                            if (size < double.Epsilon)
                            {
                                break;
                            }
                        }
                    }

                    if (size > double.Epsilon)
                    {
                        // we filled all drives up to the brim but were unable to store everything
                        if (warp_buffer < double.Epsilon)
                        {
                            // warp buffer is empty, so lets store the rest there
                            warp_buffer = size;
                            size        = 0;
                        }
                        else
                        {
                            // warp buffer not empty. that's ok if we didn't get new data
                            if (coverage_delta < double.Epsilon)
                            {
                                size = 0;
                            }
                            // else we're scanning too fast. stop.
                        }
                    }

                    // we filled all drives up to the brim but were unable to store everything
                    // cancel scanning and annoy the user
                    if (size > double.Epsilon || ec.amount < double.Epsilon)
                    {
                        warp_buffer = 0;
                        SCANsat.StopScanner(vessel, scanner, part_prefab);
                        vd.scansat_id.Add(p.flightID);
                        if (vd.cfg_ec)
                        {
                            Message.Post(Lib.BuildString("SCANsat sensor was disabled on <b>", vessel.vesselName, "</b>"));
                        }
                    }
                }
                else if (vd.scansat_id.Contains(p.flightID))
                {
                    var vi = Cache.VesselInfo(vessel);
                    if (ec.level >= 0.25 && (vi.free_capacity / vi.total_capacity > 0.9))
                    {
                        SCANsat.ResumeScanner(vessel, scanner, part_prefab);
                        vd.scansat_id.Remove(p.flightID);
                        if (vd.cfg_ec)
                        {
                            Message.Post(Lib.BuildString("SCANsat sensor resumed operations on <b>", vessel.vesselName, "</b>"));
                        }
                    }
                }
            }

            Lib.Proto.Set(m, "warp_buffer", warp_buffer);
            Lib.Proto.Set(m, "body_coverage", body_coverage);
            Lib.Proto.Set(m, "body_name", body_name);
        }