public static double StoreFile(Vessel vessel, SubjectData subjectData, double size, bool include_private = false) { if (size < double.Epsilon) { return(0); } // store what we can var drives = GetDrives(vessel, include_private); drives.Insert(0, Cache.WarpCache(vessel)); foreach (var d in drives) { var available = d.FileCapacityAvailable(); var chunk = Math.Min(size, available); if (!d.Record_file(subjectData, chunk, true)) { break; } size -= chunk; if (size < double.Epsilon) { break; } } return(size); }
// 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); } double now = Planetarium.GetUniversalTime(); double maxXmitValue = -1; string result = string.Empty; // 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)) { foreach (var p in drive.files) { if (drive.GetFileSend(p.Key) && (p.Value.ts + 3 < now || p.Value.size > min_file_size)) { // prioritize whichever file has the most science points per byte var xmitValue = Value(p.Key, p.Value.size) / p.Value.size; if (string.IsNullOrEmpty(result) || xmitValue > maxXmitValue) { result = p.Key; maxXmitValue = xmitValue; } } } } return(result); }
// 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); }
// 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); } } }
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); }
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); }
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(); }
public static void Update(Vessel v, VesselData vd, ResourceInfo ec, double elapsed_s) { if (!Lib.IsVessel(v)) { return; } // EC consumption is handled in Science update Cache.WarpCache(v).dataCapacity = vd.deviceTransmit ? vd.Connection.rate * elapsed_s : 0.0; // do nothing if network is not ready if (!NetworkInitialized) { return; } // maintain and send messages // - do not send messages during/after solar storms // - do not send messages for EVA kerbals if (!v.isEVA && v.situation != Vessel.Situations.PRELAUNCH) { if (!vd.msg_signal && !vd.Connection.linked) { vd.msg_signal = true; if (vd.cfg_signal) { string subtext = Local.UI_transmissiondisabled; switch (vd.Connection.status) { case LinkStatus.plasma: subtext = Local.UI_Plasmablackout; break; case LinkStatus.storm: subtext = Local.UI_Stormblackout; break; default: if (vd.CrewCount == 0) { switch (Settings.UnlinkedControl) { case UnlinkedCtrl.none: subtext = Local.UI_noctrl; break; case UnlinkedCtrl.limited: subtext = Local.UI_limitedcontrol; break; } } break; } Message.Post(Severity.warning, Lib.BuildString(Local.UI_signallost, " <b>", v.vesselName, "</b>"), subtext); } } else if (vd.msg_signal && vd.Connection.linked) { vd.msg_signal = false; if (vd.cfg_signal) { Message.Post(Severity.relax, Lib.BuildString("<b>", v.vesselName, "</b> ", Local.UI_signalback), vd.Connection.status == LinkStatus.direct_link ? Local.UI_directlink : Lib.BuildString(Local.UI_relayby, " <b>", vd.Connection.target_name, "</b>")); } } } }
public static void Update(Vessel v, Vessel_info vi, VesselData vd, Resource_info ec, double elapsed_s) { if (!Lib.IsVessel(v)) { return; } // consume ec for transmitters ec.Consume(vi.connection.ec * elapsed_s, "comms"); Cache.WarpCache(v).dataCapacity = vi.connection.rate * elapsed_s; // do nothing if network is not ready if (!NetworkInitialized) { return; } // maintain and send messages // - do not send messages during/after solar storms // - do not send messages for EVA kerbals if (!v.isEVA && v.situation != Vessel.Situations.PRELAUNCH) { if (!vd.msg_signal && !vi.connection.linked) { vd.msg_signal = true; if (vd.cfg_signal) { string subtext = Localizer.Format("#KERBALISM_UI_transmissiondisabled"); switch (vi.connection.status) { case LinkStatus.plasma: subtext = Localizer.Format("#KERBALISM_UI_Plasmablackout"); break; case LinkStatus.storm: subtext = Localizer.Format("#KERBALISM_UI_Stormblackout"); break; default: if (vi.crew_count == 0) { switch (Settings.UnlinkedControl) { case UnlinkedCtrl.none: subtext = Localizer.Format("#KERBALISM_UI_noctrl"); break; case UnlinkedCtrl.limited: subtext = Localizer.Format("#KERBALISM_UI_limitedcontrol"); break; } } break; } Message.Post(Severity.warning, Lib.BuildString(Localizer.Format("#KERBALISM_UI_signallost"), " <b>", v.vesselName, "</b>"), subtext); } } else if (vd.msg_signal && vi.connection.linked) { vd.msg_signal = false; if (vd.cfg_signal) { Message.Post(Severity.relax, Lib.BuildString("<b>", v.vesselName, "</b> ", Localizer.Format("#KERBALISM_UI_signalback")), vi.connection.status == LinkStatus.direct_link ? Localizer.Format("#KERBALISM_UI_directlink") : Lib.BuildString(Localizer.Format("#KERBALISM_UI_relayby"), " <b>", vi.connection.target_name, "</b>")); } } } }