Пример #1
0
        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);
        }
Пример #2
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);
            }

            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);
        }
Пример #3
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);
        }
Пример #4
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);
                }
            }
        }
Пример #5
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);
        }
Пример #6
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);
        }
Пример #7
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();
        }
Пример #8
0
        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>"));
                    }
                }
            }
        }
Пример #9
0
        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>"));
                    }
                }
            }
        }