void UpdateVesselInMasterStatusDisplay(Vessel vessel) { if (!masterStatus.ContainsKey(vessel.id)) { return; } var allPartsStatus = masterStatus[vessel.id].allPartsStatus; for (var i = 0; i < allPartsStatus.Count; i++) { var status = allPartsStatus[i]; ITestFlightCore core = status.flightCore; // Update the part status status.partStatus = core.GetPartStatus(); status.failures = core.GetActiveFailures(); status.flightData = core.GetFlightData(); double failureRate = core.GetBaseFailureRate(); MomentaryFailureRate momentaryFailureRate = core.GetWorstMomentaryFailureRate(); if (momentaryFailureRate.valid && momentaryFailureRate.failureRate > failureRate) { failureRate = momentaryFailureRate.failureRate; } status.momentaryFailureRate = failureRate; status.mtbfString = core.FailureRateToMTBFString(failureRate, TestFlightUtil.MTBFUnits.SECONDS, 999); status.runningTime = TestFlightUtil.FormatTime(core.GetBurnTime(), TestFlightUtil.TIMEFORMAT.SHORT_IDENTIFIER, false); allPartsStatus[i] = status; } }
public override void OnUpdate() { base.OnUpdate(); if (!TestFlightEnabled) { return; } // NEW RELIABILITY CODE float operatingTime = core.GetOperatingTime(); if (operatingTime < lastCheck + 1f) { return; } lastCheck = operatingTime; double baseFailureRate = core.GetBaseFailureRate(); MomentaryFailureRate momentaryFailureRate = core.GetWorstMomentaryFailureRate(); double currentFailureRate; if (momentaryFailureRate.valid && momentaryFailureRate.failureRate > baseFailureRate) { currentFailureRate = momentaryFailureRate.failureRate; } else { currentFailureRate = baseFailureRate; } // Given we have been operating for a given number of seconds, calculate our chance of survival to that time based on currentFailureRate // This is *not* an exact science, as the real calculations are much more complex than this, plus technically the momentary rate for // *each* second should be accounted for, but this is a simplification of the system. It provides decent enough numbers for fun gameplay // with chance of failure increasing exponentially over time as it approaches the *current* MTBF // S() is survival chance, f is currentFailureRate // S(t) = e^(-f*t) float reliability = Mathf.Exp((float)-currentFailureRate * (float)operatingTime); // double survivalChance = Mathf.Pow(Mathf.Exp(1), (float)currentFailureRate * (float)operatingTime * -0.693f); double survivalChance = reliability / lastReliability; lastReliability = reliability; // float failureRoll = Mathf.Min(UnityEngine.Random.Range(0f, 1f), UnityEngine.Random.Range(0f, 1f)); // float failureRoll = UnityEngine.Random.Range(0f, 1f); double failureRoll = core.RandomGenerator.NextDouble(); Log(String.Format("Survival Chance at Time {0:F2} is {1:f4}. Rolled {2:f4}", (float)operatingTime, survivalChance, failureRoll)); if (failureRoll > survivalChance) { // Debug.Log(String.Format("TestFlightReliability: Survival Chance at Time {0:F2} is {1:f4} -- {2:f4}^({3:f4}*{0:f2}*-1.0)", (float)operatingTime, survivalChance, Mathf.Exp(1), (float)currentFailureRate)); Log(String.Format("Part has failed after {1:F1} secodns of operation at MET T+{2:F2} seconds with roll of {0:F4}", failureRoll, operatingTime, this.vessel.missionTime)); core.TriggerFailure(); } }
// Get the base or static failure rate public static double GetBaseFailureRate(Part part, string alias) { ITestFlightCore core = TestFlightInterface.GetCore(part, alias); if (core == null) { return(TestFlightUtil.MIN_FAILURE_RATE); } return(core.GetBaseFailureRate()); }
internal override void Update() { if (!isReady) { return; } if (!tfScenario.SettingsEnabled) { return; } if (masterStatus == null) { masterStatus = new Dictionary <Guid, MasterStatusItem>(); } currentUTC = Planetarium.GetUniversalTime(); // ensure out vessel list is up to date Profiler.BeginSample("CacheVessels"); CacheVessels(); Profiler.EndSample(); if (currentUTC >= lastMasterStatusUpdate + tfScenario.userSettings.masterStatusUpdateFrequency) { lastMasterStatusUpdate = currentUTC; Profiler.BeginSample("VerifyMasterStatus"); VerifyMasterStatus(); Profiler.EndSample(); } // process vessels Profiler.BeginSample("ProcessVessels"); knownVesselsEnumerator = knownVessels.GetEnumerator(); while (knownVesselsEnumerator.MoveNext()) { KeyValuePair <Guid, double> entry = knownVesselsEnumerator.Current; Vessel vessel = null; Profiler.BeginSample("FindVessel"); for (int i = 0; i < FlightGlobals.Vessels.Count; i++) { if (FlightGlobals.Vessels[i].id == entry.Key) { vessel = FlightGlobals.Vessels[i]; } } Profiler.EndSample(); if (vessel == null) { continue; } if (vessel.loaded) { Profiler.BeginSample("ProcessParts"); List <Part> parts = vessel.Parts; for (int j = 0; j < parts.Count; j++) { // Each KSP part can be composed of N virtual parts Profiler.BeginSample("Reset Core List"); cores.Clear(); Profiler.EndSample(); Profiler.BeginSample("Get Cores"); for (int k = 0; k < parts[j].Modules.Count; k++) { ITestFlightCore core = parts[j].Modules[k] as ITestFlightCore; if (core != null && core.TestFlightEnabled) { cores.Add(core.Alias); } } Profiler.EndSample(); //cores = TestFlightInterface.GetActiveCores(vessel.parts[j]); if (cores == null || cores.Count <= 0) { continue; } Profiler.BeginSample("ProcessCores"); for (int k = 0; k < cores.Count; k++) { ITestFlightCore core = TestFlightUtil.GetCore(vessel.parts[j], cores[k]); if (core == null) { continue; } // Poll for flight data and part status if (!(currentUTC >= lastDataPoll + tfScenario.userSettings.masterStatusUpdateFrequency)) { continue; } // Update or Add part status in Master Status if (masterStatus.ContainsKey(vessel.id)) { Profiler.BeginSample("MasterStatus Existing Vessel"); // Vessel is already in the Master Status, so check if part is in there as well int existingPartIndex = -1; Profiler.BeginSample("Find Part"); for (int msIndex = 0; msIndex < masterStatus[vessel.id].allPartsStatus.Count; msIndex++) { if (masterStatus[vessel.id].allPartsStatus[msIndex].partID != vessel.parts[j].flightID) { continue; } existingPartIndex = msIndex; break; } Profiler.EndSample(); if (existingPartIndex > -1) { Profiler.BeginSample("Existing Part"); //PartStatus partStatus = masterStatus[vessel.id].allPartsStatus[existingPartIndex]; PartStatus partStatus = new PartStatus(); partStatus.lastSeen = currentUTC; partStatus.flightCore = core; partStatus.partName = core.Title; partStatus.partID = vessel.parts[j].flightID; Profiler.BeginSample("Part - GetPartStatus"); partStatus.partStatus = core.GetPartStatus(); Profiler.EndSample(); // get any failures Profiler.BeginSample("Part - GetActiveFailures"); partStatus.failures = core.GetActiveFailures(); Profiler.EndSample(); Profiler.BeginSample("Part - GetFlightData"); partStatus.flightData = core.GetFlightData(); Profiler.EndSample(); Profiler.BeginSample("Part - GetBaseFailureRate"); double failureRate = core.GetBaseFailureRate(); Profiler.EndSample(); Profiler.BeginSample("Part - GetWorstMomentaryFailureRate"); MomentaryFailureRate momentaryFailureRate = core.GetWorstMomentaryFailureRate(); if (momentaryFailureRate.valid && momentaryFailureRate.failureRate > failureRate) { failureRate = momentaryFailureRate.failureRate; } Profiler.EndSample(); partStatus.momentaryFailureRate = failureRate; partStatus.acknowledged = false; Profiler.BeginSample("Part - FailureRateToMTBFString"); core.FailureRateToMTBFString(failureRate, TestFlightUtil.MTBFUnits.SECONDS, false, 999, out partStatus.mtbfString); //partStatus.mtbfString = core.FailureRateToMTBFString(failureRate, TestFlightUtil.MTBFUnits.SECONDS, 999); Profiler.EndSample(); masterStatus[vessel.id].allPartsStatus[existingPartIndex] = partStatus; Profiler.EndSample(); } else { Profiler.BeginSample("New Part"); PartStatus partStatus = new PartStatus(); partStatus.lastSeen = currentUTC; partStatus.flightCore = core; partStatus.partName = core.Title; partStatus.partID = vessel.parts[j].flightID; partStatus.partStatus = core.GetPartStatus(); // get any failures partStatus.failures = core.GetActiveFailures(); partStatus.flightData = core.GetFlightData(); double failureRate = core.GetBaseFailureRate(); MomentaryFailureRate momentaryFailureRate = core.GetWorstMomentaryFailureRate(); if (momentaryFailureRate.valid && momentaryFailureRate.failureRate > failureRate) { failureRate = momentaryFailureRate.failureRate; } partStatus.momentaryFailureRate = failureRate; partStatus.acknowledged = false; core.FailureRateToMTBFString(failureRate, TestFlightUtil.MTBFUnits.SECONDS, false, 999, out partStatus.mtbfString); masterStatus[vessel.id].allPartsStatus.Add(partStatus); Profiler.EndSample(); } Profiler.EndSample(); } else { Profiler.BeginSample("MasterStatus New Vessel"); // Vessel is not in the Master Status so create a new entry for it and add this part PartStatus partStatus = new PartStatus(); partStatus.lastSeen = currentUTC; partStatus.flightCore = core; partStatus.partName = core.Title; partStatus.partID = vessel.parts[j].flightID; partStatus.partStatus = core.GetPartStatus(); // get any failures partStatus.failures = core.GetActiveFailures(); partStatus.flightData = core.GetFlightData(); double failureRate = core.GetBaseFailureRate(); MomentaryFailureRate momentaryFailureRate = core.GetWorstMomentaryFailureRate(); if (momentaryFailureRate.valid && momentaryFailureRate.failureRate > failureRate) { failureRate = momentaryFailureRate.failureRate; } partStatus.momentaryFailureRate = failureRate; partStatus.acknowledged = false; partStatus.mtbfString = core.FailureRateToMTBFString(failureRate, TestFlightUtil.MTBFUnits.SECONDS, 999); MasterStatusItem masterStatusItem = new MasterStatusItem(); masterStatusItem.vesselID = vessel.id; masterStatusItem.vesselName = vessel.GetName(); masterStatusItem.allPartsStatus = new List <PartStatus>(); masterStatusItem.allPartsStatus.Add(partStatus); masterStatus.Add(vessel.id, masterStatusItem); Profiler.EndSample(); } } Profiler.EndSample(); } } Profiler.EndSample(); if (currentUTC >= lastDataPoll + tfScenario.userSettings.minTimeBetweenDataPoll) { lastDataPoll = currentUTC; } if (currentUTC >= lastFailurePoll + tfScenario.userSettings.minTimeBetweenFailurePoll) { lastFailurePoll = currentUTC; } } Profiler.EndSample(); }
internal override void DrawWindow(int id) { if (!isReady) { return; } if (SelectedPart == null) { return; } ITestFlightCore core = null; GUILayout.BeginVertical(); GUILayout.Label(String.Format("Selected Part: {0}", selectedAlias), Styles.styleEditorTitle); float flightData = TestFlightManagerScenario.Instance.GetFlightDataForPartName(selectedAlias); if (flightData < 0f) { flightData = 0f; } core = TestFlightUtil.GetCore(SelectedPart, selectedAlias); if (core != null) { core.InitializeFlightData(flightData); GUILayout.BeginHorizontal(); double failureRate = core.GetBaseFailureRate(); Log(String.Format("Failure rate is {0:f2}", failureRate)); String mtbfString = core.FailureRateToMTBFString(failureRate, TestFlightUtil.MTBFUnits.SECONDS, 999); GUILayout.Label(String.Format("{0,-7:F2}<b>du</b>", flightData), GUILayout.Width(75)); GUILayout.Label(String.Format("{0,-5:F2} MTBF", mtbfString), GUILayout.Width(125)); GUILayout.EndHorizontal(); Log("Checking for RnD Status"); string partName = selectedAlias; float maxRnDData = core.GetMaximumRnDData(); if (flightData >= maxRnDData) { Log("Part has reached Max RnD"); GUILayout.Label("Part flight data meets or exceeds maximum lab R&D amount", Styles.styleEditorText); } else { Log("Part is RnD Eligible"); if (!tfRnDScenario.IsPartBeingResearched(partName)) { Log("Part is not being researched. Show research buttons"); int frequency = (int)tfRnDScenario.updateFrequency; Log(String.Format("Update Frequency is {0}", frequency)); TimeSpan span = new TimeSpan(0, 0, frequency); string cycleString; if (span.TotalDays > 1d) { cycleString = String.Format("{0:F2} days", span.TotalDays); } else { cycleString = String.Format("{0:F2} day", span.TotalDays); } GUILayout.Label("Hire Research Team", Styles.styleEditorTitle); GUILayout.Label("Research teams deduct funds and add flight data to your part", Styles.styleEditorText); GUILayout.Label("A research cycle lasts <b>" + cycleString + "</b>", Styles.styleEditorText); GUILayout.Label("At the end of each cycle, funds will be deducted and data added", Styles.styleEditorText); GUILayout.Label("The costs and research rate varies by team", Styles.styleEditorText); List <TestFlightRNDTeamSettings> teams = tfRnDScenario.GetAvailableTeams(); if (teams != null) { int numTeams = teams.Count; for (int i = 0; i < numTeams; i++) { GUILayout.BeginHorizontal(); if (GUILayout.Button(String.Format("Hire Team"), GUILayout.Width(100))) { tfRnDScenario.AddResearchTeam(SelectedPart, selectedAlias, i); } Log(String.Format("cycle is {0}", cycleString)); float points = teams[i].points; float cost = teams[i].costFactor * points; points = points * core.GetRnDRate(); cost = cost * core.GetRnDCost(); GUILayout.Label(String.Format("<b>{0,7:F2}</b> data, <b>{1,7:F2}</b> funds", points, cost), Styles.styleEditorTextAligned); GUILayout.EndHorizontal(); } } } else { Log("Part is already being researched. Show button to stop"); if (GUILayout.Button("Stop Research", GUILayout.Width(200))) { tfRnDScenario.RemoveResearch(partName); } } } } if (DrawToggle(ref tfScenario.userSettings.editorWindowLocked, "Lock Window", Styles.styleToggle)) { if (tfScenario.userSettings.editorWindowLocked) { CalculateWindowBounds(); tfScenario.userSettings.editorWindowPosition = WindowRect; DragEnabled = false; } else { DragEnabled = true; } tfScenario.userSettings.Save(); } GUILayout.EndVertical(); }
internal override void Update() { if (!isReady) { return; } if (masterStatus == null) { masterStatus = new Dictionary <Guid, MasterStatusItem>(); } currentUTC = Planetarium.GetUniversalTime(); // ensure out vessel list is up to date CacheVessels(); if (currentUTC >= lastMasterStatusUpdate + tfScenario.userSettings.masterStatusUpdateFrequency) { lastMasterStatusUpdate = currentUTC; VerifyMasterStatus(); } // process vessels foreach (var entry in knownVessels) { Vessel vessel = FlightGlobals.Vessels.Find(v => v.id == entry.Key); if (vessel.loaded) { foreach (Part part in vessel.parts) { ITestFlightCore core = TestFlightUtil.GetCore(part); if (core != null) { // Poll for flight data and part status if (currentUTC >= lastDataPoll + tfScenario.userSettings.masterStatusUpdateFrequency) { TestFlightData currentFlightData = new TestFlightData(); currentFlightData.scope = core.GetScope(); currentFlightData.flightData = core.GetFlightData(); currentFlightData.flightTime = core.GetFlightTime(); PartStatus partStatus = new PartStatus(); partStatus.flightCore = core; partStatus.partName = TestFlightUtil.GetPartTitle(part); partStatus.partID = part.flightID; partStatus.flightData = currentFlightData.flightData; partStatus.flightTime = currentFlightData.flightTime; partStatus.partStatus = core.GetPartStatus(); partStatus.timeToRepair = core.GetRepairTime(); double failureRate = core.GetBaseFailureRate(); MomentaryFailureRate momentaryFailureRate = core.GetWorstMomentaryFailureRate(); if (momentaryFailureRate.valid && momentaryFailureRate.failureRate > failureRate) { failureRate = momentaryFailureRate.failureRate; } partStatus.momentaryFailureRate = failureRate; partStatus.repairRequirements = core.GetRequirementsTooltip(); partStatus.acknowledged = core.IsFailureAcknowledged(); partStatus.activeFailure = core.GetFailureModule(); partStatus.mtbfString = core.FailureRateToMTBFString(failureRate, TestFlightUtil.MTBFUnits.SECONDS, 999); // Update or Add part status in Master Status if (masterStatus.ContainsKey(vessel.id)) { // Vessel is already in the Master Status, so check if part is in there as well int numItems = masterStatus[vessel.id].allPartsStatus.Count(p => p.partID == part.flightID); int existingPartIndex; if (numItems == 1) { existingPartIndex = masterStatus[vessel.id].allPartsStatus.FindIndex(p => p.partID == part.flightID); masterStatus[vessel.id].allPartsStatus[existingPartIndex] = partStatus; } else if (numItems == 0) { masterStatus[vessel.id].allPartsStatus.Add(partStatus); } else { existingPartIndex = masterStatus[vessel.id].allPartsStatus.FindIndex(p => p.partID == part.flightID); masterStatus[vessel.id].allPartsStatus[existingPartIndex] = partStatus; Log("[ERROR] TestFlightManager: Found " + numItems + " matching parts in Master Status Display!"); } } else { // Vessel is not in the Master Status so create a new entry for it and add this part MasterStatusItem masterStatusItem = new MasterStatusItem(); masterStatusItem.vesselID = vessel.id; masterStatusItem.vesselName = vessel.GetName(); masterStatusItem.allPartsStatus = new List <PartStatus>(); masterStatusItem.allPartsStatus.Add(partStatus); masterStatus.Add(vessel.id, masterStatusItem); } PartFlightData data = tfScenario.GetFlightDataForPartName(TestFlightUtil.GetFullPartName(part)); if (data != null) { data.AddFlightData(part.name, currentFlightData); } else { data = new PartFlightData(); data.AddFlightData(TestFlightUtil.GetFullPartName(part), currentFlightData); tfScenario.SetFlightDataForPartName(TestFlightUtil.GetFullPartName(part), data); } } } } } if (currentUTC >= lastDataPoll + tfScenario.userSettings.minTimeBetweenDataPoll) { lastDataPoll = currentUTC; } if (currentUTC >= lastFailurePoll + tfScenario.userSettings.minTimeBetweenFailurePoll) { lastFailurePoll = currentUTC; } } }
internal override void DrawWindow(int id) { if (!isReady) { return; } if (SelectedPart == null) { GUILayout.BeginVertical(); GUILayout.Label("Select a part to display its details", Styles.styleEditorTitle); GUILayout.Label("MouseOver part in bin or 3D view to quickview", Styles.styleEditorText); GUILayout.Label("RightClick part in bin (not 3D) to toggle window lock on that part", Styles.styleEditorText); GUILayout.EndVertical(); if (DrawToggle(ref tfScenario.userSettings.editorWindowLocked, "Lock Window", Styles.styleToggle)) { if (tfScenario.userSettings.editorWindowLocked) { CalculateWindowBounds(); tfScenario.userSettings.editorWindowPosition = WindowRect; DragEnabled = false; } else { DragEnabled = true; } tfScenario.userSettings.Save(); } return; } ITestFlightCore core = null; GUILayout.BeginVertical(); GUILayout.Label(String.Format("Selected Part: {0}", TestFlightUtil.GetFullPartName(SelectedPart)), Styles.styleEditorTitle); tfScenario.userSettings.currentEditorScrollPosition = GUILayout.BeginScrollView(tfScenario.userSettings.currentEditorScrollPosition); TestFlightPartData partData = tfScenario.GetPartDataForPart(TestFlightUtil.GetFullPartName(SelectedPart)); if (partData != null) { float flightData = partData.GetFloat("flightData"); core = TestFlightUtil.GetCore(SelectedPart); if (core != null) { core.InitializeFlightData(flightData); GUILayout.BeginHorizontal(); double failureRate = core.GetBaseFailureRate(); String mtbfString = core.FailureRateToMTBFString(failureRate, TestFlightUtil.MTBFUnits.SECONDS, 999); // 10 characters for body max plus 10 characters for situation plus underscore = 21 characters needed for longest scope string GUILayout.Label(String.Format("{0,-7:F2}<b>du</b>", flightData), GUILayout.Width(75)); GUILayout.Label(String.Format("{0,-5:F2} MTBF", mtbfString), GUILayout.Width(125)); GUILayout.EndHorizontal(); } } GUILayout.EndScrollView(); if (DrawToggle(ref tfScenario.userSettings.editorWindowLocked, "Lock Window", Styles.styleToggle)) { if (tfScenario.userSettings.editorWindowLocked) { CalculateWindowBounds(); tfScenario.userSettings.editorWindowPosition = WindowRect; DragEnabled = false; } else { DragEnabled = true; } tfScenario.userSettings.Save(); } GUILayout.EndVertical(); }
internal override void Update() { if (!isReady) { return; } if (masterStatus == null) { masterStatus = new Dictionary <Guid, MasterStatusItem>(); } currentUTC = Planetarium.GetUniversalTime(); // ensure out vessel list is up to date CacheVessels(); if (currentUTC >= lastMasterStatusUpdate + tfScenario.userSettings.masterStatusUpdateFrequency) { lastMasterStatusUpdate = currentUTC; VerifyMasterStatus(); } // process vessels foreach (var entry in knownVessels) { Vessel vessel = FlightGlobals.Vessels.Find(v => v.id == entry.Key); if (vessel.loaded) { foreach (Part part in vessel.parts) { // Each KSP part can be composed of N virtual parts List <string> cores = TestFlightInterface.GetActiveCores(part); if (cores == null || cores.Count <= 0) { continue; } foreach (string activeCore in cores) { ITestFlightCore core = TestFlightUtil.GetCore(part, activeCore); if (core != null) { // Poll for flight data and part status if (currentUTC >= lastDataPoll + tfScenario.userSettings.masterStatusUpdateFrequency) { // Old data structure deprecated v1.3 PartStatus partStatus = new PartStatus(); partStatus.lastSeen = currentUTC; partStatus.flightCore = core; partStatus.partName = core.Title; partStatus.partID = part.flightID; partStatus.partStatus = core.GetPartStatus(); // get any failures partStatus.failures = core.GetActiveFailures(); partStatus.flightData = core.GetFlightData(); double failureRate = core.GetBaseFailureRate(); MomentaryFailureRate momentaryFailureRate = core.GetWorstMomentaryFailureRate(); if (momentaryFailureRate.valid && momentaryFailureRate.failureRate > failureRate) { failureRate = momentaryFailureRate.failureRate; } partStatus.momentaryFailureRate = failureRate; partStatus.acknowledged = false; partStatus.mtbfString = core.FailureRateToMTBFString(failureRate, TestFlightUtil.MTBFUnits.SECONDS, 999); // Update or Add part status in Master Status if (masterStatus.ContainsKey(vessel.id)) { // Vessel is already in the Master Status, so check if part is in there as well int numItems = masterStatus[vessel.id].allPartsStatus.Count(p => p.partID == part.flightID); int existingPartIndex; if (numItems == 1) { existingPartIndex = masterStatus[vessel.id].allPartsStatus.FindIndex(p => p.partID == part.flightID); masterStatus[vessel.id].allPartsStatus[existingPartIndex] = partStatus; } else if (numItems == 0) { masterStatus[vessel.id].allPartsStatus.Add(partStatus); } else { existingPartIndex = masterStatus[vessel.id].allPartsStatus.FindIndex(p => p.partID == part.flightID); masterStatus[vessel.id].allPartsStatus[existingPartIndex] = partStatus; Log("[ERROR] TestFlightManager: Found " + numItems + " matching parts in Master Status Display!"); } } else { // Vessel is not in the Master Status so create a new entry for it and add this part MasterStatusItem masterStatusItem = new MasterStatusItem(); masterStatusItem.vesselID = vessel.id; masterStatusItem.vesselName = vessel.GetName(); masterStatusItem.allPartsStatus = new List <PartStatus>(); masterStatusItem.allPartsStatus.Add(partStatus); masterStatus.Add(vessel.id, masterStatusItem); } } } } } } if (currentUTC >= lastDataPoll + tfScenario.userSettings.minTimeBetweenDataPoll) { lastDataPoll = currentUTC; } if (currentUTC >= lastFailurePoll + tfScenario.userSettings.minTimeBetweenFailurePoll) { lastFailurePoll = currentUTC; } } }