public static void Failman(this Panel p, Vessel v) { // 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 data VesselData vd = v.KerbalismData(); // if not a valid vessel, leave the panel empty if (!vd.IsSimulated) { return; } // set metadata p.Title(Lib.BuildString(Lib.Ellipsis(v.vesselName, Styles.ScaleStringLength(20)), " ", Lib.Color("Quality Management", Lib.Kolor.LightGrey))); p.Width(Styles.ScaleWidthFloat(355.0f)); p.paneltype = Panel.PanelType.failures; string section = string.Empty; // get devices List <ReliabilityInfo> devices = vd.ReliabilityStatus(); int deviceCount = 0; // for each device foreach (var ri in devices) { if (section != Group2Section(ri.group)) { section = Group2Section(ri.group); p.AddSection(section); } string status = StatusString(ri); // render device entry p.AddContent( label: ri.title, value: status, hover: () => Highlighter.Set(ri.partId, Color.blue)); deviceCount++; } // no devices case if (deviceCount == 0) { p.AddContent("<i>no quality info</i>"); } }
// set highlighting static void Highlight(Part p) { if (DB.Vessel(p.vessel).cfg_highlights) { // get state among all reliability components in the part bool broken = false; bool critical = false; foreach (Reliability m in p.FindModulesImplementing <Reliability>()) { broken |= m.broken; critical |= m.critical; } if (broken) { Highlighter.Set(p.flightID, !critical ? Color.yellow : Color.red); } } }
static void Render_sample(Panel p, uint partId, 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(ExperimentInfo.Situation(filename), 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>", ExperimentInfo.Situation(filename), "</color>" ); double exp_value = Science.Value(filename, sample.size); if (exp_value >= 0.1) { 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, (Action)null, () => Highlighter.Set(partId, Color.cyan)); 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(filename), "?"), new DialogGUIButton("Dump it", () => drive.samples.Remove(filename)), new DialogGUIButton("Keep it", () => { })); } ); }
static void Render_file(Panel p, uint partId, string filename, File file, Drive drive, bool short_strings, Vessel v) { // get experiment info ExperimentInfo exp = Science.Experiment(filename); double rate = Cache.VesselInfo(v).connection.rate; // 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(ExperimentInfo.Situation(filename), 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>", ExperimentInfo.Situation(filename), "</color>" ); double exp_value = Science.Value(filename, file.size); if (exp_value >= 0.1) { 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, (Action)null, () => Highlighter.Set(partId, Color.cyan)); bool send = drive.GetFileSend(filename); p.AddIcon(send ? Icons.send_cyan : Icons.send_black, "Flag the file for transmission to <b>DSN</b>", () => { drive.Send(filename, !send); }); p.AddIcon(Icons.toggle_red, "Delete the file", () => { Lib.Popup("Warning!", Lib.BuildString("Do you really want to delete ", exp.FullName(filename), "?"), new DialogGUIButton("Delete it", () => drive.Delete_file(filename, double.MaxValue, v.protoVessel)), new DialogGUIButton("Keep it", () => { })); } ); }
public static void Devman(this Panel p, Vessel v) { // 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(20)), " <color=#cccccc>" + Localizer.Format("#KERBALISM_UI_devman") + "</color>")); p.Width(Styles.ScaleWidthFloat(355.0f)); p.paneltype = Panel.PanelType.scripts; // time-out simulation if (!Lib.IsControlUnit(v) && p.Timeout(vi)) { return; } // get devices Dictionary <uint, Device> devices = Computer.Boot(v); int deviceCount = 0; // direct control if (script_index == 0) { // draw section title and desc p.AddSection ( Localizer.Format("#KERBALISM_UI_devices"), Description(), () => p.Prev(ref script_index, (int)ScriptType.last), () => p.Next(ref script_index, (int)ScriptType.last), true ); // for each device foreach (var pair in devices) { // render device entry Device dev = pair.Value; if (!dev.IsVisible()) { continue; } p.AddContent(dev.Name(), dev.Info(), string.Empty, dev.Toggle, () => Highlighter.Set(dev.Part(), Color.cyan)); deviceCount++; } } // script editor else { // get script ScriptType script_type = (ScriptType)script_index; string script_name = script_type.ToString().Replace('_', ' ').ToUpper(); Script script = DB.Vessel(v).computer.Get(script_type); // draw section title and desc p.AddSection ( script_name, Description(), () => p.Prev(ref script_index, (int)ScriptType.last), () => p.Next(ref script_index, (int)ScriptType.last), true ); // for each device foreach (var pair in devices) { Device dev = pair.Value; if (!dev.IsVisible()) { continue; } // determine tribool state int state = !script.states.ContainsKey(pair.Key) ? -1 : !script.states[pair.Key] ? 0 : 1; // render device entry p.AddContent ( dev.Name(), state == -1 ? "<color=#999999>" + Localizer.Format("#KERBALISM_UI_dontcare") + " </color>" : state == 0 ? "<color=red>" + Localizer.Format("#KERBALISM_Generic_OFF") + "</color>" : "<color=cyan>" + Localizer.Format("#KERBALISM_Generic_ON") + "</color>", string.Empty, () => { switch (state) { case -1: script.Set(dev, true); break; case 0: script.Set(dev, null); break; case 1: script.Set(dev, false); break; } }, () => Highlighter.Set(dev.Part(), Color.cyan) ); deviceCount++; } } // no devices case if (deviceCount == 0) { p.AddContent("<i>no devices</i>"); } }
static void Render_sample(Panel p, uint partId, Sample sample, Drive drive, bool short_strings) { // render experiment name string exp_label = Lib.BuildString ( "<b>", Lib.Ellipsis(sample.subjectData.ExperimentTitle, Styles.ScaleStringLength(short_strings ? 24 : 38)), "</b> <size=", Styles.ScaleInteger(10).ToString(), ">", Lib.Ellipsis(sample.subjectData.SituationTitle, Styles.ScaleStringLength((short_strings ? 32 : 62) - Lib.Ellipsis(sample.subjectData.ExperimentTitle, Styles.ScaleStringLength(short_strings ? 24 : 38)).Length)), "</size>" ); string exp_tooltip = Lib.BuildString ( sample.subjectData.ExperimentTitle, "\n", Lib.Color(sample.subjectData.SituationTitle, Lib.Kolor.LightGrey) ); double exp_value = sample.size * sample.subjectData.SciencePerMB; if (exp_value >= 0.1) { exp_tooltip = Lib.BuildString(exp_tooltip, "\n<b>", Lib.HumanReadableScience(exp_value, false), "</b>"); } if (sample.mass > Double.Epsilon) { exp_tooltip = Lib.BuildString(exp_tooltip, "\n<b>", Lib.HumanReadableMass(sample.mass), "</b>"); } if (!string.IsNullOrEmpty(sample.resultText)) { exp_tooltip = Lib.BuildString(exp_tooltip, "\n", Lib.WordWrapAtLength(sample.resultText, 50)); } p.AddContent(exp_label, Lib.HumanReadableSampleSize(sample.size), exp_tooltip, (Action)null, () => Highlighter.Set(partId, Color.cyan)); p.AddRightIcon(sample.analyze ? Textures.lab_cyan : Textures.lab_black, Local.FILEMANAGER_analysis, () => { sample.analyze = !sample.analyze; }); //"Flag the file for analysis in a <b>laboratory</b>" p.AddRightIcon(Textures.toggle_red, Local.FILEMANAGER_Dumpsample, () => //"Dump the sample" { Lib.Popup(Local.FILEMANAGER_Warning_title, //"Warning!" Local.FILEMANAGER_DumpConfirm.Format(sample.subjectData.FullTitle), //"Do you really want to dump <<1>>?", new DialogGUIButton(Local.FILEMANAGER_DumpConfirm_button1, () => drive.Delete_sample(sample.subjectData)), //"Dump it" new DialogGUIButton(Local.FILEMANAGER_DumpConfirm_button2, () => { })); //"Keep it" } ); }
static void Render_file(Panel p, uint partId, File file, Drive drive, bool short_strings, Vessel v) { // render experiment name string exp_label = Lib.BuildString ( "<b>", Lib.Ellipsis(file.subjectData.ExperimentTitle, Styles.ScaleStringLength(short_strings ? 24 : 38)), "</b> <size=", Styles.ScaleInteger(10).ToString(), ">", Lib.Ellipsis(file.subjectData.SituationTitle, Styles.ScaleStringLength((short_strings ? 32 : 62) - Lib.Ellipsis(file.subjectData.ExperimentTitle, Styles.ScaleStringLength(short_strings ? 24 : 38)).Length)), "</size>" ); string exp_tooltip = Lib.BuildString ( file.subjectData.ExperimentTitle, "\n", Lib.Color(file.subjectData.SituationTitle, Lib.Kolor.LightGrey) ); double exp_value = file.size * file.subjectData.SciencePerMB; if (file.subjectData.ScienceRemainingToRetrieve > 0f && file.size > 0.0) { exp_tooltip = Lib.BuildString(exp_tooltip, "\n<b>", Lib.HumanReadableScience(exp_value, false), "</b>"); } if (file.transmitRate > 0.0) { if (file.size > 0.0) { exp_tooltip = Lib.Color(Lib.BuildString(exp_tooltip, "\n", Local.FILEMANAGER_TransmittingRate.Format(Lib.HumanReadableDataRate(file.transmitRate)), " : <i>", Lib.HumanReadableCountdown(file.size / file.transmitRate), "</i>"), Lib.Kolor.Cyan); //Transmitting at <<1>> } else { exp_tooltip = Lib.Color(Lib.BuildString(exp_tooltip, "\n", Local.FILEMANAGER_TransmittingRate.Format(Lib.HumanReadableDataRate(file.transmitRate))), Lib.Kolor.Cyan); //Transmitting at <<1>> } } else if (v.KerbalismData().Connection.rate > 0.0) { exp_tooltip = Lib.BuildString(exp_tooltip, "\n", Local.FILEMANAGER_Transmitduration, "<i>", Lib.HumanReadableDuration(file.size / v.KerbalismData().Connection.rate), "</i>"); //Transmit duration : } if (!string.IsNullOrEmpty(file.resultText)) { exp_tooltip = Lib.BuildString(exp_tooltip, "\n", Lib.WordWrapAtLength(file.resultText, 50)); } string size; if (file.transmitRate > 0.0) { if (file.size == 0.0) { size = Lib.Color(Lib.BuildString("↑ ", Lib.HumanReadableDataRate(file.transmitRate)), Lib.Kolor.Cyan); } else { size = Lib.Color(Lib.BuildString("↑ ", Lib.HumanReadableDataSize(file.size)), Lib.Kolor.Cyan); } } else { size = Lib.HumanReadableDataSize(file.size); } p.AddContent(exp_label, size, exp_tooltip, (Action)null, () => Highlighter.Set(partId, Color.cyan)); bool send = drive.GetFileSend(file.subjectData.Id); p.AddRightIcon(send ? Textures.send_cyan : Textures.send_black, Local.FILEMANAGER_send, () => { drive.Send(file.subjectData.Id, !send); }); //"Flag the file for transmission to <b>DSN</b>" p.AddRightIcon(Textures.toggle_red, Local.FILEMANAGER_Delete, () => //"Delete the file" { Lib.Popup(Local.FILEMANAGER_Warning_title, //"Warning!" Local.FILEMANAGER_DeleteConfirm.Format(file.subjectData.FullTitle), //Lib.BuildString(, "?"),//"Do you really want to delete <<1>>", new DialogGUIButton(Local.FILEMANAGER_DeleteConfirm_button1, () => drive.Delete_file(file.subjectData)), //"Delete it" new DialogGUIButton(Local.FILEMANAGER_DeleteConfirm_button2, () => { })); //"Keep it" } ); }
public static void Devman(this Panel p, Vessel v) { // 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 data VesselData vd = v.KerbalismData(); // if not a valid vessel, leave the panel empty if (!vd.IsSimulated) { return; } // set metadata p.Title(Lib.BuildString(Lib.Ellipsis(v.vesselName, Styles.ScaleStringLength(20)), Lib.Color("#KERBALISM_UI_devman", Lib.Kolor.LightGrey))); p.Width(Styles.ScaleWidthFloat(355.0f)); p.paneltype = Panel.PanelType.scripts; // time-out simulation if (!Lib.IsControlUnit(v) && p.Timeout(vd)) { return; } // get devices List <Device> devices = Computer.GetModuleDevices(v); int deviceCount = 0; // direct control if (script_index == 0) { // draw section title and desc p.AddSection ( Localizer.Format("#KERBALISM_UI_devices"), Description(), () => p.Prev(ref script_index, (int)ScriptType.last), () => p.Next(ref script_index, (int)ScriptType.last), false ); bool hasVesselDeviceSection = false; bool hasModuleDeviceSection = false; // for each device for (int i = devices.Count - 1; i >= 0; i--) { Device dev = devices[i]; dev.OnUpdate(); if (!dev.IsVisible) { continue; } // create vessel device section if necessary if (dev is VesselDevice) { if (!hasVesselDeviceSection) { p.AddSection("VESSEL DEVICES"); hasVesselDeviceSection = true; } } // create module device section if necessary else { if (!hasModuleDeviceSection) { p.AddSection("MODULE DEVICES"); hasModuleDeviceSection = true; } } if (dev.PartId != 0u) { p.AddContent(dev.DisplayName, dev.Status, dev.Tooltip, dev.Toggle, () => Highlighter.Set(dev.PartId, Color.cyan)); } else { p.AddContent(dev.DisplayName, dev.Status, dev.Tooltip, dev.Toggle); } if (dev.Icon != null) { p.SetLeftIcon(dev.Icon.texture, dev.Icon.tooltip, dev.Icon.onClick); } deviceCount++; } } // script editor else { // get script ScriptType script_type = (ScriptType)script_index; string script_name = script_type.ToString().Replace('_', ' ').ToUpper(); Script script = v.KerbalismData().computer.Get(script_type); // draw section title and desc p.AddSection ( script_name, Description(), () => p.Prev(ref script_index, (int)ScriptType.last), () => p.Next(ref script_index, (int)ScriptType.last) ); bool hasVesselDeviceSection = false; bool hasModuleDeviceSection = false; // for each device for (int i = devices.Count - 1; i >= 0; i--) { Device dev = devices[i]; dev.OnUpdate(); if (!dev.IsVisible) { continue; } // determine tribool state int state = !script.states.ContainsKey(dev.Id) ? -1 : !script.states[dev.Id] ? 0 : 1; // create vessel device section if necessary if (dev is VesselDevice) { if (!hasVesselDeviceSection) { p.AddSection("VESSEL DEVICES"); hasVesselDeviceSection = true; } } // create module device section if necessary else { if (!hasModuleDeviceSection) { p.AddSection("MODULE DEVICES"); hasModuleDeviceSection = true; } } // render device entry p.AddContent ( dev.DisplayName, state == -1 ? Lib.Color(Localizer.Format("#KERBALISM_UI_dontcare"), Lib.Kolor.DarkGrey) : Lib.Color(state == 0, Localizer.Format("#KERBALISM_Generic_OFF"), Lib.Kolor.Yellow, Localizer.Format("#KERBALISM_Generic_ON"), Lib.Kolor.Green), string.Empty, () => { switch (state) { case -1: script.Set(dev, true); break; case 0: script.Set(dev, null); break; case 1: script.Set(dev, false); break; } }, () => Highlighter.Set(dev.PartId, Color.cyan) ); deviceCount++; } } // no devices case if (deviceCount == 0) { p.AddContent("<i>no devices</i>"); } }
public static void DevMan(this Panel p, Vessel v) { // 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, 24), " <color=#cccccc>DEV MANAGER</color>")); // time-out simulation if (p.Timeout(vi)) { return; } // get devices Dictionary <uint, Device> devices = Computer.Boot(v); // direct control if (script_index == 0) { // draw section title and desc p.SetSection ( "DEVICES", Description(), () => p.Prev(ref script_index, (int)ScriptType.last), () => p.Next(ref script_index, (int)ScriptType.last) ); // for each device foreach (var pair in devices) { // render device entry Device dev = pair.Value; p.SetContent(dev.Name(), dev.Info(), string.Empty, dev.Toggle, () => Highlighter.Set(dev.Part(), Color.cyan)); } } // script editor else { // get script ScriptType script_type = (ScriptType)script_index; string script_name = script_type.ToString().Replace('_', ' ').ToUpper(); Script script = DB.Vessel(v).computer.Get(script_type); // draw section title and desc p.SetSection ( script_name, Description(), () => p.Prev(ref script_index, (int)ScriptType.last), () => p.Next(ref script_index, (int)ScriptType.last) ); // for each device foreach (var pair in devices) { // determine tribool state int state = !script.states.ContainsKey(pair.Key) ? -1 : !script.states[pair.Key] ? 0 : 1; // render device entry Device dev = pair.Value; p.SetContent ( dev.Name(), state == -1 ? "<color=#999999>don't care</color>" : state == 0 ? "<color=red>off</color>" : "<color=cyan>on</color>", string.Empty, () => { switch (state) { case -1: script.Set(dev, true); break; case 0: script.Set(dev, null); break; case 1: script.Set(dev, false); break; } }, () => Highlighter.Set(dev.Part(), Color.cyan) ); } } // no devices case if (devices.Count == 0) { p.SetContent("<i>no devices</i>"); } }
public static void NetMan(this Panel p, Vessel v) { // 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, 20), " <color=#cccccc>NETWORK INFO</color>")); // time-out simulation #if !DEBUG if (p.Timeout(vi)) { return; } #endif p.SetSection("ADAPTORS"); p.Set_IsFreqSelector(true); // store all devices var devices = new Dictionary <uint, NetDevice>(); // store device being added NetDevice adap; // loaded vessel if (v.loaded) { foreach (NetworkAdaptor m in Lib.FindModules <NetworkAdaptor>(v)) { adap = new NetAdaptorDevice(m); // add the device // - multiple same-type components in the same part will have the same id, and are ignored if (!devices.ContainsKey(adap.Id())) { devices.Add(adap.Id(), adap); } } } else { // store data required to support multiple modules of same type in a part var PD = new Dictionary <string, Lib.module_prefab_data>(); // for each part foreach (ProtoPartSnapshot proto in v.protoVessel.protoPartSnapshots) { // get part prefab (required for module properties) Part part_prefab = PartLoader.getPartInfoByName(proto.partName).partPrefab; // get all module prefabs var module_prefabs = part_prefab.FindModulesImplementing <PartModule>(); // clear module indexes PD.Clear(); // for each module foreach (ProtoPartModuleSnapshot m in proto.modules) { // get the module prefab // if the prefab doesn't contain this module, skip it PartModule module_prefab = Lib.ModulePrefab(module_prefabs, m.moduleName, PD); if (!module_prefab) { continue; } // if the module is disabled, skip it // note: this must be done after ModulePrefab is called, so that indexes are right if (!Lib.Proto.GetBool(m, "isEnabled")) { continue; } if (m.moduleName == "NetworkAdaptor") { adap = new ProtoNetAdaptorDevice(m, proto.flightID, v); // add the device // - multiple same-type components in the same part will have the same id, and are ignored if (!devices.ContainsKey(adap.Id())) { devices.Add(adap.Id(), adap); } } } } } // dict order by device name // for each device foreach (var pair in devices.OrderBy(x => x.Value.Name())) { // render device entry NetDevice dev = pair.Value; // Get how many antennas share the same freq AntennasByFrequency x = null; if (vi.antenna.antennasByFreq.ContainsKey(dev.InfoFreq())) { x = vi.antenna.antennasByFreq[dev.InfoFreq()]; } p.SetContent(dev.Name(), dev.InfoRate(), string.Empty, null, () => Highlighter.Set(dev.Part(), Color.cyan), dev.InfoFreq()); p.SetIcon(Icons.left_freq, "Decrease", () => { if (dev.InfoFreq() > 0) // && x != null { //if (x.antCount == 1 && x.countConnections > 0) //{ // Lib.Popup( // "Warning!", // Lib.BuildString("This is the last antenna on '", dev.InfoFreq().ToString(), // "' frequency.\nYou will lost connection in this frequency.\nDo you really want to remove this frequency from this vessel?"), // new DialogGUIButton("Remove", () => dev.ChangeFreq(-1)), // new DialogGUIButton("Keep it", () => { })); //} //else dev.ChangeFreq(-1); } }); p.SetIcon(Icons.right_freq, "Increase", () => { if (dev.InfoFreq() < 99) // && x != null { //if (x.antCount == 1 && x.countConnections > 0) //{ // Lib.Popup( // "Warning!", // Lib.BuildString("This is the last antenna on '", dev.InfoFreq().ToString(), // "' frequency.\nYou will lost connection in this frequency.\nDo you really want to remove this frequency from this vessel?"), // new DialogGUIButton("Remove", () => dev.ChangeFreq(+1)), // new DialogGUIButton("Keep it", () => { })); //} //else dev.ChangeFreq(+1); } }); } p.SetSection("FREQUENCY(S) DETAIL"); foreach (short key in vi.antenna.antennasByFreq.Keys) { double range = vi.antenna.antennasByFreq[key].antennaPower; double rate = vi.antenna.antennasByFreq[key].antennaRate; Render_ConnectionDetail(p, range, rate, key); } }