diff --git a/Telecom/connection_availability.cs b/Telecom/connection_availability.cs index 14600f9..a02e534 100644 --- a/Telecom/connection_availability.cs +++ b/Telecom/connection_availability.cs @@ -1,6 +1,8 @@ using System; using System.Collections.Generic; +using System.Linq; using ContractConfigurator; +using ContractConfigurator.Parameters; using Contracts; using RealAntennas; @@ -46,7 +48,7 @@ public override ConnectionAvailability.Goal Goal() { } } - public class BroadcastRxAvailability : ContractParameter { + public class BroadcastRxAvailability : ContractConfiguratorParameter { public BroadcastRxAvailability(Service service, AvailabilityMetric metric, Monitor monitor, @@ -72,20 +74,20 @@ protected override void OnUpdate() { return; } if (metric_.partial) { - SetIncomplete(); + SetState(ParameterState.Incomplete); } else if (metric_.availability < availability_) { if (goal_ == ConnectionAvailability.Goal.MAINTAIN) { - SetFailed(); + SetState(ParameterState.Failed); } else { - SetIncomplete(); + SetState(ParameterState.Incomplete); } } else { - SetComplete(); + SetState(ParameterState.Complete); } GetTitle(); } - protected override string GetTitle() { + protected override string GetParameterTitle() { string status = service_.available ? "Currently connected" : "Currently disconnected"; @@ -100,6 +102,9 @@ protected override string GetTitle() { return title; } + protected override void OnParameterLoad(ConfigNode node) { } + protected override void OnParameterSave(ConfigNode node) { } + private Service service_; private AvailabilityMetric metric_; private Monitor monitor_; @@ -110,7 +115,7 @@ protected override string GetTitle() { private TitleTracker title_tracker_; } - public class ConnectionAvailability : ContractParameter { + public class ConnectionAvailability : ContractConfiguratorParameter { public enum Goal { ACHIEVE, MAINTAIN, @@ -136,6 +141,19 @@ public ConnectionAvailability(string connection, monitoring_definition_ = monitoring_definition; } + private static string ForceGetStationName(string name) { + // Gets the station's readable name, even if it isn't in the network yet. **Can be very slow!!** + // Attempt, in order: + // - Get from the active Network + // - Read from config + if (Telecom.Instance.network.GetStation(name) is RealAntennas.Network.RACommNetHome home) { + return home.displaynodeName; + } else { + return Network.GetStationDefinition(name).GetValue("objectName"); + // This was pretty bad before caching Station definitions. + } + } + protected override void OnUpdate() { base.OnUpdate(); if (goal_ == Goal.MAINTAIN) { @@ -149,32 +167,32 @@ protected override void OnUpdate() { any_incomplete |= subparameter.State == ParameterState.Incomplete; } if (any_failed) { - SetFailed(); + SetState(ParameterState.Failed); } else if (any_incomplete) { - SetIncomplete(); + SetState(ParameterState.Incomplete); } else { - SetComplete(); + SetState(ParameterState.Complete); } } else { if (state == ParameterState.Failed) { return; } if (metric.partial) { - SetIncomplete(); + SetState(ParameterState.Incomplete); } else if (metric.availability < availability_) { if (goal_ == Goal.MAINTAIN) { - SetFailed(); + SetState(ParameterState.Failed); } else { - SetIncomplete(); + SetState(ParameterState.Incomplete); } } else { - SetComplete(); + SetState(ParameterState.Complete); } } - GetTitle(); + GetParameterTitle(); } - protected override void OnLoad(ConfigNode node) { + protected override void OnParameterLoad(ConfigNode node) { connection_ = node.GetValue("connection"); availability_ = double.Parse(node.GetValue("availability")); latency_ = node.HasValue("latency") @@ -189,9 +207,11 @@ protected override void OnLoad(ConfigNode node) { } metric_definition_ = node.GetNode("metric"); monitoring_definition_ = node.GetNode("monitoring"); + preview_string_ = node.HasValue("preview") + ? node.GetValue("preview") : null; } - protected override void OnSave(ConfigNode node) { + protected override void OnParameterSave(ConfigNode node) { node.AddValue("connection", connection_); node.AddValue("availability", availability_); if (latency_ != null) { @@ -200,9 +220,10 @@ protected override void OnSave(ConfigNode node) { node.AddValue("goal", goal_); node.AddNode("metric", metric_definition_); node.AddNode("monitoring", monitoring_definition_); + node.AddValue("preview", preview_string); } - protected override string GetTitle() { + protected override string GetParameterTitle() { var connection = Telecom.Instance.network.GetConnection(connection_); string data_rate = RATools.PrettyPrintDataRate(connection.data_rate); double latency = latency_ ?? connection.latency_limit; @@ -256,6 +277,11 @@ protected override string GetTitle() { return title; } + protected override string GetParameterTitlePreview(out bool hideChildren) { + hideChildren = false; + return preview_string; + } + private AvailabilityMetric MakeMetric(ConfigNode definition) { string type = definition.GetValue("type"); if (type == "monthly") { @@ -403,6 +429,69 @@ private List subparameters { } } + private string preview_string { + get { + if (!(preview_string_ is null)) { + return preview_string_; + } + //if (goal_ == Goal.MAINTAIN) { + // return null; // Don't bother generating for maintenance contracts + //} + + // We need a couple things to preview the connection, and they should ideally never change. + + double data_rate; + double latency; + string tx = null; + string[] rxs = null; + string[] trxs = null; + + if (Telecom.Instance.network.GetConnectionSafe(connection_) is Connection connection) { + // The sane option. + data_rate = connection.data_rate; + latency = connection.latency_limit; + if (connection is PointToMultipointConnection point_to_multipoint) { + tx = point_to_multipoint.tx_name; + rxs = point_to_multipoint.rx_names; + } else if (connection is DuplexConnection duplex) { + trxs = duplex.trx_names; + } + } else { + // The stupid option. + ConfigNode conn = Network.GetConnectionDefinition(connection_); + data_rate = double.Parse(conn.GetValue("rate")); + latency = latency_ ?? double.Parse(conn.GetValue("latency")); + if (conn.HasValue("tx")) { + tx = conn.GetValue("tx"); + rxs = conn.GetValues("rx"); + } else { + trxs = conn.GetValues("trx"); + } + } + string pretty_rate = RATools.PrettyPrintDataRate(data_rate); + string pretty_latency = latency >= 1 ? $"{latency} s" : $"{latency * 1000} ms"; + + if (!(tx is null)) { + if (rxs.Length > 1) { + return $"Support broadcast from {ForceGetStationName(tx)} to " + + $"{string.Join(", ", rxs.Take(rxs.Length - 1).Select(rx => ForceGetStationName(rx)))}" + + $" and {ForceGetStationName(rxs[rxs.Length - 1])}, with a data rate of {pretty_rate} and a " + + $"latency of at most {pretty_latency}. Target availability: {availability_:P2}"; + // Don't add subparameters just for preview purposes, we'll just list them out instead. + } else { + return $"Support transmission from {ForceGetStationName(tx)} to " + + $"{ForceGetStationName(rxs[0])}, with a data rate of {pretty_rate} and a " + + $"latency of at most {pretty_latency}. Target availability: {availability_:P2}"; + } + } else { + return $"Support duplex communication between {ForceGetStationName(trxs[0])} " + + $"and {ForceGetStationName(trxs[1])}, with a one-way data rate of " + + $"{pretty_rate} and a round-trip latency of at most " + + $"{pretty_latency}. Target availability: {availability_:P2}"; + } + } + } + public string connection_name => connection_; private List subparameters_; @@ -416,5 +505,6 @@ private List subparameters { private Goal goal_; private string last_title_; private TitleTracker title_tracker_; + private string preview_string_; } } diff --git a/Telecom/network.cs b/Telecom/network.cs index 6331681..8bd2ebd 100644 --- a/Telecom/network.cs +++ b/Telecom/network.cs @@ -6,22 +6,34 @@ namespace σκοπός { public class Network { - static ConfigNode GetStationDefinition(string name) { + public static ConfigNode GetStationDefinition(string name) { + if (station_definition_cache_.ContainsKey(name)) { + return station_definition_cache_[name]; + } else { foreach (var block in GameDatabase.Instance.GetConfigs("skopos_telecom")) { foreach (var definition in block.config.GetNodes("station")) { - if (definition.GetValue("name") == name) { - return definition; + var station_name = definition.GetValue("name"); + station_definition_cache_[station_name] = definition; + if (station_name == name) { + return definition; + } } } } throw new KeyNotFoundException($"No definition for station {name}"); } - static ConfigNode GetConnectionDefinition(string name) { + public static ConfigNode GetConnectionDefinition(string name) { + if (connection_definition_cache_.ContainsKey(name)) { + return connection_definition_cache_[name]; + } else { foreach (var block in GameDatabase.Instance.GetConfigs("skopos_telecom")) { foreach (var definition in block.config.GetNodes("connection")) { - if (definition.GetValue("name") == name) { - return definition; + var connection_name = definition.GetValue("name"); + connection_definition_cache_[connection_name] = definition; + if (connection_name == name) { + return definition; + } } } } @@ -226,8 +238,18 @@ public Connection GetConnection(string name) { } } + public Connection GetConnectionSafe(string name) { + if (!connections_.TryGetValue(name, out var connection)) { + return null; + } + return connection; + } + public RACommNetHome GetStation(string name) { - return stations_[name]; + if (!stations_.TryGetValue(name, out var station)) { + return null; + } + return station; } public IEnumerable AllGround() { @@ -246,6 +268,10 @@ public IEnumerable AllGround() { public string[] names_ = { }; public Routing routing_ = new Routing(); + private static readonly Dictionary station_definition_cache_ = new Dictionary(); + private static readonly Dictionary connection_definition_cache_ = new Dictionary(); + // purposefully don't save these two, we want these to invalidate on game relaunch + public Dictionary> connections_by_contract { get; } = new Dictionary>(); public HashSet contracted_connections { get; } = new HashSet();