Revert "ARMapRenderer: Removed vesselFrameCache because it was never doing any good."
This reverts commit 0c941dfb7171ef27c234553484b88e8eea12286a.
--- a/ARConfiguration.cs
+++ b/ARConfiguration.cs
@@ -37,6 +37,12 @@
private set;
}
+ public static bool PrettyLines
+ {
+ get;
+ private set;
+ }
+
private bool showConfigWindow;
private Rect configWindowPos;
@@ -80,6 +86,8 @@
this.LoadConfigValue("requireConnectionForControl", false);
ARConfiguration.FixedPowerCost = this.LoadConfigValue("fixedPowerCost", false);
+
+ ARConfiguration.PrettyLines = this.LoadConfigValue("drawPrettyLines", true);
GameEvents.onGameSceneLoadRequested.Add(this.onSceneChangeRequested);
@@ -184,6 +192,17 @@
GUILayout.EndHorizontal();
+ GUILayout.BeginHorizontal();
+
+ bool prettyLines = GUITools.Toggle(ARConfiguration.PrettyLines, "Draw Pretty Lines");
+ if (prettyLines != ARConfiguration.PrettyLines)
+ {
+ ARConfiguration.PrettyLines = prettyLines;
+ this.SaveConfigValue("drawPrettyLines", prettyLines);
+ }
+
+ GUILayout.EndHorizontal();
+
if (requireLineOfSight)
{
GUILayout.BeginHorizontal();
--- a/ARFlightController.cs
+++ b/ARFlightController.cs
@@ -41,9 +41,14 @@
protected Dictionary<ConnectionStatus, string> connectionTextures;
protected Dictionary<ConnectionStatus, Texture> appLauncherTextures;
+ protected ARMapRenderer mapRenderer;
+
protected IButton toolbarButton;
protected ApplicationLauncherButton appLauncherButton;
+ protected Tools.DebugLogger log;
+
+ protected System.Diagnostics.Stopwatch updateTimer;
#endregion
#region Properties
@@ -114,6 +119,10 @@
protected void Awake()
{
this.lockID = "ARConnectionRequired";
+
+ this.log = Tools.DebugLogger.New(this);
+
+ this.updateTimer = new System.Diagnostics.Stopwatch();
this.connectionTextures = new Dictionary<ConnectionStatus, string>();
@@ -144,6 +153,11 @@
GameEvents.onVesselChange.Add(this.onVesselChange);
}
+ protected void Start()
+ {
+ this.mapRenderer = MapView.MapCamera.gameObject.AddComponent<ARMapRenderer>();
+ }
+
protected void FixedUpdate()
{
if (this.appLauncherButton == null && !ToolbarManager.ToolbarAvailable && ApplicationLauncher.Ready)
@@ -154,7 +168,7 @@
);
}
- Tools.DebugLogger log = Tools.DebugLogger.New(this);
+ this.log.Clear();
VesselCommand availableCommand;
@@ -203,6 +217,22 @@
InputLockManager.RemoveControlLock(this.lockID);
}
+ log.Print();
+ }
+
+ protected void Update()
+ {
+ if (!this.updateTimer.IsRunning || this.updateTimer.ElapsedMilliseconds > 125L)
+ {
+ this.updateTimer.Reset();
+ }
+ else
+ {
+ return;
+ }
+
+ this.log.Clear();
+
if (
(this.toolbarButton != null || this.appLauncherButton != null) &&
HighLogic.LoadedSceneIsFlight &&
@@ -211,62 +241,7 @@
{
log.Append("Checking vessel relay status.\n");
- List<ModuleLimitedDataTransmitter> relays =
- FlightGlobals.ActiveVessel.getModulesOfType<ModuleLimitedDataTransmitter>();
-
- log.AppendFormat("\t...found {0} relays\n", relays.Count);
-
- bool vesselCanTransmit = false;
- bool vesselHasOptimalRelay = false;
-
- foreach (ModuleLimitedDataTransmitter relay in relays)
- {
- log.AppendFormat("\tvesselCanTransmit: {0}, vesselHasOptimalRelay: {1}\n",
- vesselCanTransmit, vesselHasOptimalRelay);
-
- log.AppendFormat("\tChecking relay {0}\n" +
- "\t\tCanTransmit: {1}, transmitDistance: {2}, nominalRange: {3}\n",
- relay,
- relay.CanTransmit(),
- relay.transmitDistance,
- relay.nominalRange
- );
-
- bool relayCanTransmit = relay.CanTransmit();
-
- if (!vesselCanTransmit && relayCanTransmit)
- {
- vesselCanTransmit = true;
- }
-
- if (!vesselHasOptimalRelay &&
- relayCanTransmit &&
- relay.transmitDistance <= (double)relay.nominalRange)
- {
- vesselHasOptimalRelay = true;
- }
-
- if (vesselCanTransmit && vesselHasOptimalRelay)
- {
- break;
- }
- }
-
- log.AppendFormat("Done checking. vesselCanTransmit: {0}, vesselHasOptimalRelay: {1}\n",
- vesselCanTransmit, vesselHasOptimalRelay);
-
- if (vesselHasOptimalRelay)
- {
- this.currentConnectionStatus = ConnectionStatus.Optimal;
- }
- else if (vesselCanTransmit)
- {
- this.currentConnectionStatus = ConnectionStatus.Suboptimal;
- }
- else
- {
- this.currentConnectionStatus = ConnectionStatus.None;
- }
+ this.currentConnectionStatus = FlightGlobals.ActiveVessel.GetConnectionStatus();
log.AppendFormat("currentConnectionStatus: {0}, setting texture to {1}",
this.currentConnectionStatus, this.currentConnectionTexture);
@@ -297,6 +272,11 @@
{
InputLockManager.RemoveControlLock(this.lockID);
+ if (this.mapRenderer != null)
+ {
+ GameObject.Destroy(this.mapRenderer);
+ }
+
if (this.toolbarButton != null)
{
this.toolbarButton.Destroy();
@@ -327,13 +307,6 @@
InputLockManager.RemoveControlLock(this.lockID);
}
#endregion
-
- public enum ConnectionStatus
- {
- None,
- Suboptimal,
- Optimal
- }
}
}
--- /dev/null
+++ b/ARMapRenderer.cs
@@ -1,1 +1,253 @@
-
+// AntennaRange
+//
+// ARMapRenderer.cs
+//
+// Copyright © 2014, toadicus
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without modification,
+// are permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice,
+// this list of conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright notice,
+// this list of conditions and the following disclaimer in the documentation and/or other
+// materials provided with the distribution.
+//
+// 3. Neither the name of the copyright holder nor the names of its contributors may be used
+// to endorse or promote products derived from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+using KSP;
+using System;
+using System.Collections.Generic;
+using ToadicusTools;
+using UnityEngine;
+
+namespace AntennaRange
+{
+ public class ARMapRenderer : MonoBehaviour
+ {
+ #region Fields
+ private Dictionary<Guid, LineRenderer> vesselLineRenderers;
+ private Dictionary<Guid, bool> vesselFrameCache;
+ #endregion
+
+ #region Properties
+ public LineRenderer this[Guid idx]
+ {
+ get
+ {
+ if (this.vesselLineRenderers == null)
+ {
+ this.vesselLineRenderers = new Dictionary<Guid, LineRenderer>();
+ }
+
+ if (!this.vesselLineRenderers.ContainsKey(idx))
+ {
+ GameObject obj = new GameObject();
+ obj.layer = 31;
+
+ LineRenderer lr = obj.AddComponent<LineRenderer>();
+
+ lr.SetColors(Color.green, Color.green);
+ lr.material = MapView.OrbitLinesMaterial;
+ lr.SetVertexCount(2);
+
+ this.vesselLineRenderers[idx] = lr;
+ }
+
+ return this.vesselLineRenderers[idx];
+ }
+ }
+ #endregion
+
+ #region MonoBehaviour Lifecycle
+ private void Awake()
+ {
+ if (ARConfiguration.PrettyLines)
+ {
+ this.vesselLineRenderers = new Dictionary<Guid, LineRenderer>();
+ this.vesselFrameCache = new Dictionary<Guid, bool>();
+ }
+ }
+
+ private void OnPreCull()
+ {
+ if (!HighLogic.LoadedSceneIsFlight || !MapView.MapIsEnabled || !ARConfiguration.PrettyLines)
+ {
+ this.Cleanup();
+
+ return;
+ }
+
+ Tools.DebugLogger log = Tools.DebugLogger.New(this);
+
+ try
+ {
+ log.AppendFormat("OnPreCull.\n");
+
+ log.AppendFormat("\tMapView: Draw3DLines: {0}\n" +
+ "\tMapView.MapCamera.camera.fieldOfView: {1}\n" +
+ "\tMapView.MapCamera.Distance: {2}\n",
+ MapView.Draw3DLines,
+ MapView.MapCamera.camera.fieldOfView,
+ MapView.MapCamera.Distance
+ );
+
+ this.vesselFrameCache.Clear();
+
+ log.AppendLine("vesselFrameCache cleared.");
+
+ if (FlightGlobals.ready && FlightGlobals.Vessels != null)
+ {
+ log.AppendLine("FlightGlobals ready and Vessels list not null.");
+
+ foreach (Vessel vessel in FlightGlobals.Vessels)
+ {
+ if (vessel == null)
+ {
+ log.AppendFormat("Skipping vessel {0} altogether because it is null.\n");
+ continue;
+ }
+
+ log.AppendFormat("Checking vessel {0}.\n", vessel.vesselName);
+
+ switch (vessel.vesselType)
+ {
+ case VesselType.Debris:
+ case VesselType.EVA:
+ case VesselType.Unknown:
+ case VesselType.SpaceObject:
+ log.AppendFormat("\tDiscarded because vessel is of invalid type {0}\n",
+ vessel.vesselType);
+ continue;
+ }
+
+ IAntennaRelay vesselRelay = vessel.GetBestRelay();
+
+ if (vesselRelay != null)
+ {
+ this.SetRelayVertices(vesselRelay);
+ }
+ }
+ }
+ }
+ catch (Exception)
+ {
+ this.Cleanup();
+ }
+ #if DEBUG
+ finally
+ {
+ log.Print();
+ }
+ #endif
+ }
+
+ private void OnDestroy()
+ {
+ this.Cleanup();
+
+ print("ARMapRenderer: Destroyed.");
+ }
+ #endregion
+
+ private void SetRelayVertices(IAntennaRelay relay)
+ {
+ if (relay == null)
+ {
+ return;
+ }
+
+ LineRenderer renderer = this[relay.vessel.id];
+
+ Vector3d start;
+ Vector3d end;
+
+ renderer.enabled = true;
+
+ if (!relay.CanTransmit())
+ {
+ renderer.SetColors(Color.red, Color.red);
+ }
+ else
+ {
+ if (relay.transmitDistance < relay.nominalTransmitDistance)
+ {
+ renderer.SetColors(Color.green, Color.green);
+ }
+ else
+ {
+ renderer.SetColors(Color.yellow, Color.yellow);
+ }
+ }
+
+ start = ScaledSpace.LocalToScaledSpace(relay.vessel.GetWorldPos3D());
+
+ if (relay.KerbinDirect)
+ {
+ end = ScaledSpace.LocalToScaledSpace(AntennaRelay.Kerbin.position);
+ }
+ else
+ {
+ if (relay.targetRelay == null)
+ {
+ return;
+ }
+ end = ScaledSpace.LocalToScaledSpace(relay.targetRelay.vessel.GetWorldPos3D());
+ }
+
+ float lineWidth;
+
+ if (MapView.Draw3DLines)
+ {
+ lineWidth = 0.005859375f * MapView.MapCamera.Distance;
+ }
+ else
+ {
+ lineWidth = 2f;
+
+ start = MapView.MapCamera.camera.WorldToScreenPoint(start);
+ end = MapView.MapCamera.camera.WorldToScreenPoint(end);
+
+ float d = Screen.height / 2f + 0.01f;
+ start.z = start.z >= 0f ? d : -d;
+ end.z = end.z >= 0f ? d : -d;
+ }
+
+ renderer.SetWidth(lineWidth, lineWidth);
+
+ renderer.SetPosition(0, start);
+ renderer.SetPosition(1, end);
+ }
+
+ public void Cleanup()
+ {
+ if (this.vesselLineRenderers != null && this.vesselLineRenderers.Count > 0)
+ {
+ foreach (LineRenderer lineRenderer in this.vesselLineRenderers.Values)
+ {
+ lineRenderer.enabled = false;
+ GameObject.Destroy(lineRenderer.gameObject);
+ }
+ this.vesselLineRenderers.Clear();
+ }
+
+ if (this.vesselFrameCache != null && this.vesselFrameCache.Count > 0)
+ {
+ this.vesselFrameCache.Clear();
+ }
+ }
+ }
+}
+
+
--- a/AntennaRange.csproj
+++ b/AntennaRange.csproj
@@ -78,6 +78,7 @@
<Compile Include="RelayExtensions.cs" />
<Compile Include="ARConfiguration.cs" />
<Compile Include="ARFlightController.cs" />
+ <Compile Include="ARMapRenderer.cs" />
</ItemGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
<ItemGroup>
--- a/AntennaRelay.cs
+++ b/AntennaRelay.cs
@@ -31,17 +31,20 @@
using System.Linq;
using ToadicusTools;
-// @DONE TODO: Retool nearestRelay to always contain the nearest relay, even if out of range.
-// @DONE TODO: Retool CanTransmit to not rely on nearestRelay == null.
-// TODO: Track occluded vessels somehow.
-
namespace AntennaRange
{
+ /// <summary>
+ /// Relay code at the heart of AntennaRange
+ /// </summary>
public class AntennaRelay
{
// We don't have a Bard, so we'll hide Kerbin here.
private static CelestialBody _Kerbin;
- protected static CelestialBody Kerbin
+
+ /// <summary>
+ /// Fetches, caches, and returns a <see cref="CelestialBody"/> reference to Kerbin
+ /// </summary>
+ public static CelestialBody Kerbin
{
get
{
@@ -54,11 +57,19 @@
}
}
- private IAntennaRelay _nearestRelayCache;
+ private bool canTransmit;
+
+ private IAntennaRelay nearestRelay;
+ private IAntennaRelay bestOccludedRelay;
+
+ private System.Diagnostics.Stopwatch searchTimer;
+ private long millisecondsBetweenSearches;
+
+ /// <summary>
+ /// The <see cref="AntennaRange.ModuleLimitedDataTransmitter"/> reference underlying this AntennaRelay, as an
+ /// <see cref="AntennaRange.IAntennaRelay"/>
+ /// </summary>
protected IAntennaRelay moduleRef;
-
- protected System.Diagnostics.Stopwatch searchTimer;
- protected long millisecondsBetweenSearches;
/// <summary>
/// Gets the parent Vessel.
@@ -73,39 +84,24 @@
}
/// <summary>
- /// Gets or sets the nearest relay.
- /// </summary>
- /// <value>The nearest relay</value>
- public IAntennaRelay nearestRelay
- {
- get
- {
- if (!this.searchTimer.IsRunning ||
- this.searchTimer.ElapsedMilliseconds > this.millisecondsBetweenSearches)
- {
- this._nearestRelayCache = this.FindNearestRelay();
-
- this.searchTimer.Restart();
- }
-
- return this._nearestRelayCache;
- }
- protected set
- {
- this._nearestRelayCache = value;
- }
- }
-
- /// <summary>
- /// Gets the first <see cref="CelestialBody"/> found to be blocking line of sight.
- /// </summary>
- public virtual CelestialBody firstOccludingBody
+ /// Gets the target <see cref="AntennaRange.IAntennaRelay"/>relay.
+ /// </summary>
+ public IAntennaRelay targetRelay
{
get;
protected set;
}
/// <summary>
+ /// Gets the first <see cref="CelestialBody"/> found to be blocking line of sight.
+ /// </summary>
+ public virtual CelestialBody firstOccludingBody
+ {
+ get;
+ protected set;
+ }
+
+ /// <summary>
/// Gets the transmit distance.
/// </summary>
/// <value>The transmit distance.</value>
@@ -113,144 +109,111 @@
{
get
{
- // If there is no available relay nearby...
- // @DONE TODO: Remove nearestRelay == null
- double kerbinDistance = this.DistanceTo(Kerbin);
-
- if (this.nearestRelay != null)
- {
- double relayDistance = this.DistanceTo(this.nearestRelay);
-
- // If our nearest relay is nearer than Kerbin, use its distance.
- if (relayDistance < kerbinDistance)
- {
- this.KerbinDirect = false;
-
- return relayDistance;
- }
- }
-
- this.KerbinDirect = true;
-
-
- // .. return the distance to Kerbin
- return kerbinDistance;
- }
- }
-
- /// <summary>
- /// The maximum distance at which this relay can operate.
- /// </summary>
- /// <value>The max transmit distance.</value>
- public virtual float maxTransmitDistance
+ this.FindNearestRelay();
+
+ if (this.KerbinDirect || this.targetRelay == null)
+ {
+ return this.DistanceTo(Kerbin);
+ }
+ else
+ {
+ return this.DistanceTo(this.targetRelay);
+ }
+ }
+ }
+
+ /// <summary>
+ /// Gets the nominal transmit distance at which the Antenna behaves just as prescribed by Squad's config.
+ /// </summary>
+ public virtual double nominalTransmitDistance
{
get;
set;
}
/// <summary>
- /// Gets a value indicating whether this <see cref="AntennaRange.ProtoDataTransmitter"/> has been checked during
- /// the current relay attempt.
- /// </summary>
- /// <value><c>true</c> if relay checked; otherwise, <c>false</c>.</value>
- public virtual bool relayChecked
+ /// The maximum distance at which this relay can operate.
+ /// </summary>
+ /// <value>The max transmit distance.</value>
+ public virtual double maxTransmitDistance
+ {
+ get;
+ set;
+ }
+
+ /// <summary>
+ /// Gets a value indicating whether this <see cref="AntennaRange.IAntennaRelay"/> Relay is communicating
+ /// directly with Kerbin.
+ /// </summary>
+ public virtual bool KerbinDirect
{
get;
protected set;
}
- public virtual bool KerbinDirect
- {
- get;
- protected set;
- }
-
/// <summary>
/// Determines whether this instance can transmit.
/// </summary>
/// <returns><c>true</c> if this instance can transmit; otherwise, <c>false</c>.</returns>
public virtual bool CanTransmit()
{
- CelestialBody fob = null;
-
- // @DONE TODO: Remove nearestRelay == null
- // Because we're correctly falling back to Kerbin in transmitDistance the first test should always fail
- // when we're out of range of anything, and the second will fail when LOS is blocked (and enforced).
-
- // If our transmit distance is greater than our maximum range, we can't transmit and it doesn't matter why.
- if (this.transmitDistance > this.maxTransmitDistance)
- {
- this.firstOccludingBody = null;
- return false;
- }
- // ...if we're in range...
+ this.FindNearestRelay();
+ return this.canTransmit;
+ }
+
+ /// <summary>
+ /// Finds the nearest relay.
+ /// </summary>
+ /// <returns>The nearest relay or null, if no relays in range.</returns>
+ private void FindNearestRelay()
+ {
+ if (!this.searchTimer.IsRunning || this.searchTimer.ElapsedMilliseconds > this.millisecondsBetweenSearches)
+ {
+ this.searchTimer.Reset();
+ }
else
{
- // ...check for LOS problems...
- if (
- ARConfiguration.RequireLineOfSight
- && this.KerbinDirect &&
- !this.vessel.hasLineOfSightTo(Kerbin, out fob, ARConfiguration.RadiusRatio)
- )
- {
- this.firstOccludingBody = fob;
- return false;
- }
-
- this.firstOccludingBody = null;
- return true;
- }
- }
-
- /// <summary>
- /// Finds the nearest relay.
- /// </summary>
- /// <returns>The nearest relay or null, if no relays in range.</returns>
- private IAntennaRelay FindNearestRelay()
- {
- /*if (this.searchTimer.IsRunning && this.searchTimer.ElapsedMilliseconds < this.millisecondsBetweenSearches)
- {
- return this.nearestRelay;
- }
-
- if (this.searchTimer.IsRunning)
- {
- this.searchTimer.Stop();
- this.searchTimer.Reset();
- }
-
- this.searchTimer.Start();*/
-
- Tools.PostDebugMessage(string.Format(
- "{0}: finding nearest relay for {1} ({2})",
- this.GetType().Name,
- this,
- this.vessel.id
- ));
-
- this.firstOccludingBody = null;
+ return;
+ }
+
+ // Skip vessels that have already been checked for a nearest relay this pass.
+ if (RelayDatabase.Instance.CheckedVesselsTable.ContainsKey(this.vessel.id))
+ {
+ return;
+ }
+
+ if (FlightGlobals.ActiveVessel != null && FlightGlobals.ActiveVessel.id == this.vessel.id)
+ {
+ Tools.PostDebugMessage(string.Format(
+ "{0}: finding nearest relay for {1}",
+ this.GetType().Name,
+ this.ToString()
+ ));
+ }
// Set this vessel as checked, so that we don't check it again.
RelayDatabase.Instance.CheckedVesselsTable[vessel.id] = true;
- double nearestSqrDistance = double.PositiveInfinity;
-
- IAntennaRelay _nearestRelay = null;
+ // Blank everything we're trying to find before the search.
+ this.firstOccludingBody = null;
+ this.bestOccludedRelay = null;
+ this.targetRelay = null;
+ this.nearestRelay = null;
+
+ CelestialBody bodyOccludingBestOccludedRelay = null;
+
+ double nearestRelaySqrDistance = double.PositiveInfinity;
+ double bestOccludedSqrDistance = double.PositiveInfinity;
+ double maxTransmitSqrDistance = this.maxTransmitDistance * this.maxTransmitDistance;
/*
* Loop through all the vessels and exclude this vessel, vessels of the wrong type, and vessels that are too
* far away. When we find a candidate, get through its antennae for relays which have not been checked yet
- * and that can transmit. Once we find a suitable candidate, assign it to _nearestRelay for comparison
+ * and that can transmit. Once we find a suitable candidate, assign it to nearestRelay for comparison
* against future finds.
* */
foreach (Vessel potentialVessel in FlightGlobals.Vessels)
{
- // Skip vessels that have already been checked for a nearest relay this pass.
- if (RelayDatabase.Instance.CheckedVesselsTable.ContainsKey(potentialVessel.id))
- {
- continue;
- }
-
// Skip vessels of the wrong type.
switch (potentialVessel.vesselType)
{
@@ -270,74 +233,294 @@
continue;
}
+ // Find the distance from here to the vessel...
+ double potentialSqrDistance = this.sqrDistanceTo(potentialVessel);
+
+ CelestialBody fob = null;
+
// Skip vessels to which we do not have line of sight.
- CelestialBody fob = null;
-
- if (ARConfiguration.RequireLineOfSight &&
- !this.vessel.hasLineOfSightTo(potentialVessel, out fob, ARConfiguration.RadiusRatio))
+ if (
+ ARConfiguration.RequireLineOfSight &&
+ !this.vessel.hasLineOfSightTo(potentialVessel, out fob, ARConfiguration.RadiusRatio)
+ )
{
this.firstOccludingBody = fob;
- Tools.PostDebugMessage(
- this,
- "Vessel {0} discarded because we do not have line of sight.",
- potentialVessel.vesselName
- );
+
+ if (FlightGlobals.ActiveVessel != null && FlightGlobals.ActiveVessel.id == this.vessel.id)
+ {
+ Tools.PostDebugMessage("{6}: Vessel {0} discarded because we do not have line of sight." +
+ "\npotentialSqrDistance: {1}, bestOccludedSqrDistance: {2}, maxTransmitSqrDistance: {3}" +
+ "\npotentialSqrDistance < bestOccludedSqrDistance: {4}" +
+ "\npotentialSqrDistance < (this.maxTransmitDistance * this.maxTransmitDistance): {5}",
+ potentialVessel.vesselName,
+ potentialSqrDistance, bestOccludedSqrDistance, this.maxTransmitDistance * this.maxTransmitDistance,
+ potentialSqrDistance < bestOccludedSqrDistance,
+ potentialSqrDistance < (this.maxTransmitDistance * this.maxTransmitDistance),
+ this.ToString()
+ );
+ }
+
+ if (
+ (potentialSqrDistance < bestOccludedSqrDistance) &&
+ (potentialSqrDistance < maxTransmitSqrDistance)
+ )
+ {
+ if (FlightGlobals.ActiveVessel != null && FlightGlobals.ActiveVessel.id == this.vessel.id)
+ {
+ Tools.PostDebugMessage("{0}: Checking {1} relays on {2}.",
+ this.ToString(),
+ potentialVessel.GetAntennaRelays().Count(),
+ potentialVessel
+ );
+ }
+
+ foreach (IAntennaRelay occludedRelay in potentialVessel.GetAntennaRelays())
+ {
+ if (FlightGlobals.ActiveVessel != null && FlightGlobals.ActiveVessel.id == this.vessel.id)
+ {
+ Tools.PostDebugMessage(this.ToString() + " Checking candidate for bestOccludedRelay: {0}" +
+ "\n\tCanTransmit: {1}", occludedRelay, occludedRelay.CanTransmit());
+ }
+
+ if (occludedRelay.CanTransmit())
+ {
+ this.bestOccludedRelay = occludedRelay;
+ bodyOccludingBestOccludedRelay = fob;
+ bestOccludedSqrDistance = potentialSqrDistance;
+
+ if (FlightGlobals.ActiveVessel != null && FlightGlobals.ActiveVessel.id == this.vessel.id)
+ {
+ Tools.PostDebugMessage(this.ToString() + " Found new bestOccludedRelay: {0}" +
+ "\nfirstOccludingBody: {1}" +
+ "\nbestOccludedSqrDistance: {2}",
+ occludedRelay,
+ fob,
+ potentialSqrDistance
+ );
+ }
+ break;
+ }
+ }
+ }
+
continue;
}
-
- this.firstOccludingBody = null;
-
- // Find the distance from here to the vessel...
- double potentialSqrDistance = (potentialVessel.GetWorldPos3D() - vessel.GetWorldPos3D()).sqrMagnitude;
/*
* ...so that we can skip the vessel if it is further away than a vessel we've already checked.
* */
- if (potentialSqrDistance > nearestSqrDistance)
- {
- Tools.PostDebugMessage(
- this,
- "Vessel {0} discarded because it is out of range, or farther than another relay.",
- potentialVessel.vesselName
- );
+ if (potentialSqrDistance > nearestRelaySqrDistance)
+ {
+ if (FlightGlobals.ActiveVessel != null && FlightGlobals.ActiveVessel.id == this.vessel.id)
+ {
+ Tools.PostDebugMessage("{0}: Vessel {1} discarded because it is out of range, or farther than another relay.",
+ this.ToString(),
+ potentialVessel.vesselName
+ );
+ }
continue;
}
- nearestSqrDistance = potentialSqrDistance;
+ nearestRelaySqrDistance = potentialSqrDistance;
foreach (IAntennaRelay potentialRelay in potentialVessel.GetAntennaRelays())
{
- if (potentialRelay.CanTransmit())
- {
- _nearestRelay = potentialRelay;
- Tools.PostDebugMessage(string.Format("{0}: found new best relay {1} ({2})",
- this.GetType().Name,
- _nearestRelay.ToString(),
- _nearestRelay.vessel.id
- ));
+ if (potentialRelay.CanTransmit() && potentialRelay.targetRelay != this)
+ {
+ this.nearestRelay = potentialRelay;
+
+ if (FlightGlobals.ActiveVessel != null && FlightGlobals.ActiveVessel.id == this.vessel.id)
+ {
+ Tools.PostDebugMessage(string.Format("{0}: found new best relay {1} ({2})",
+ this.ToString(),
+ this.nearestRelay.ToString(),
+ this.nearestRelay.vessel.id
+ ));
+ }
break;
}
}
}
+
+ CelestialBody bodyOccludingKerbin = null;
+
+ double kerbinSqrDistance = this.vessel.DistanceTo(Kerbin) - Kerbin.Radius;
+ kerbinSqrDistance *= kerbinSqrDistance;
+
+ Tools.DebugLogger log = Tools.DebugLogger.New(this);
+
+ log.AppendFormat("{0} ({1}): Search done, figuring status.", this.ToString(), this.GetType().Name);
+
+ // If we don't have LOS to Kerbin, focus on relays
+ if (!this.vessel.hasLineOfSightTo(Kerbin, out bodyOccludingKerbin, ARConfiguration.RadiusRatio))
+ {
+ log.AppendFormat("\n\tKerbin LOS is blocked by {0}.", bodyOccludingKerbin.bodyName);
+
+ // nearestRelaySqrDistance will be infinity if all relays are occluded or none exist.
+ // Therefore, this will only be true if a valid relay is in range.
+ if (nearestRelaySqrDistance <= maxTransmitSqrDistance)
+ {
+ log.AppendFormat("\n\tCan transmit to nearby relay {0} ({1} <= {2}).",
+ this.nearestRelay == null ? "null" : this.nearestRelay.ToString(),
+ nearestRelaySqrDistance, maxTransmitSqrDistance);
+
+ this.KerbinDirect = false;
+ this.canTransmit = true;
+ this.targetRelay = this.nearestRelay;
+ }
+ // If this isn't true, we can't transmit, but pick a second best of bestOccludedRelay and Kerbin anyway
+ else
+ {
+ log.AppendFormat("\n\tCan't transmit to nearby relay {0} ({1} > {2}).",
+ this.nearestRelay == null ? "null" : this.nearestRelay.ToString(),
+ nearestRelaySqrDistance, maxTransmitSqrDistance);
+
+ this.canTransmit = false;
+
+ // If the best occluded relay is closer than Kerbin, target it.
+ if (bestOccludedSqrDistance < kerbinSqrDistance)
+ {
+ log.AppendFormat("\n\t\tPicking occluded relay {0} as target ({1} < {2}).",
+ this.bestOccludedRelay == null ? "null" : this.bestOccludedRelay.ToString(),
+ bestOccludedSqrDistance, kerbinSqrDistance);
+
+ this.KerbinDirect = false;
+ this.targetRelay = this.bestOccludedRelay;
+ this.firstOccludingBody = bodyOccludingBestOccludedRelay;
+ }
+ // Otherwise, target Kerbin and report the first body blocking it.
+ else
+ {
+ log.AppendFormat("\n\t\tPicking Kerbin as target ({0} >= {1}).",
+ bestOccludedSqrDistance, kerbinSqrDistance);
+
+ this.KerbinDirect = true;
+ this.targetRelay = null;
+ this.firstOccludingBody = bodyOccludingKerbin;
+ }
+ }
+ }
+ // If we do have LOS to Kerbin, try to prefer the closest of nearestRelay and Kerbin
+ else
+ {
+ log.AppendFormat("\n\tKerbin is in LOS.");
+
+ // If the nearest relay is closer than Kerbin and in range, transmit to it.
+ if (nearestRelaySqrDistance <= maxTransmitSqrDistance)
+ {
+ log.AppendFormat("\n\tCan transmit to nearby relay {0} ({1} <= {2}).",
+ this.nearestRelay == null ? "null" : this.nearestRelay.ToString(),
+ nearestRelaySqrDistance, maxTransmitSqrDistance);
+
+ this.canTransmit = true;
+
+ // If the nearestRelay is closer than Kerbin, use it.
+ if (nearestRelaySqrDistance < kerbinSqrDistance)
+ {
+ log.AppendFormat("\n\tPicking relay {0} over Kerbin ({1} < {2}).",
+ this.nearestRelay == null ? "null" : this.nearestRelay.ToString(),
+ nearestRelaySqrDistance, kerbinSqrDistance);
+
+ this.KerbinDirect = false;
+ this.targetRelay = this.nearestRelay;
+ }
+ // Otherwise, Kerbin is closer, so use it.
+ else
+ {
+ log.AppendFormat("\n\tBut picking Kerbin over nearby relay {0} ({1} >= {2}).",
+ this.nearestRelay == null ? "null" : this.nearestRelay.ToString(),
+ nearestRelaySqrDistance, kerbinSqrDistance);
+
+ this.KerbinDirect = true;
+ this.targetRelay = null;
+ }
+ }
+ // If the nearest relay is out of range, we still need to check on Kerbin.
+ else
+ {
+ log.AppendFormat("\n\tCan't transmit to nearby relay {0} ({1} > {2}).",
+ this.nearestRelay == null ? "null" : this.nearestRelay.ToString(),
+ nearestRelaySqrDistance, maxTransmitSqrDistance);
+
+ // If Kerbin is in range, use it.
+ if (kerbinSqrDistance <= maxTransmitSqrDistance)
+ {
+ log.AppendFormat("\n\tCan transmit to Kerbin ({0} <= {1}).",
+ kerbinSqrDistance, maxTransmitSqrDistance);
+
+ this.canTransmit = true;
+ this.KerbinDirect = true;
+ this.targetRelay = null;
+ }
+ // If Kerbin is out of range and the nearest relay is out of range, pick a second best between
+ // Kerbin and bestOccludedRelay
+ else
+ {
+ log.AppendFormat("\n\tCan't transmit to Kerbin ({0} > {1}).",
+ kerbinSqrDistance, maxTransmitSqrDistance);
+
+ this.canTransmit = false;
+
+ // If the best occluded relay is closer than Kerbin, use it.
+ // Since bestOccludedSqrDistance is infinity if there are no occluded relays,
+ // this is safe
+ if (bestOccludedSqrDistance < kerbinSqrDistance)
+ {
+ log.AppendFormat("\n\t\tPicking occluded relay {0} as target ({1} < {2}).",
+ this.bestOccludedRelay == null ? "null" : this.bestOccludedRelay.ToString(),
+ bestOccludedSqrDistance, kerbinSqrDistance);
+
+ this.KerbinDirect = false;
+ this.targetRelay = bestOccludedRelay;
+ this.firstOccludingBody = bodyOccludingBestOccludedRelay;
+ }
+ // Otherwise, target Kerbin. Since we have LOS, blank the first occluding body.
+ else
+ {
+ log.AppendFormat("\n\t\tPicking Kerbin as target ({0} >= {1}).",
+ bestOccludedSqrDistance, kerbinSqrDistance);
+
+ this.KerbinDirect = true;
+ this.targetRelay = null;
+ this.firstOccludingBody = null;
+ }
+ }
+ }
+ }
+
+ log.AppendFormat("\n{0}: Status determination complete.", this.ToString());
+
+ log.Print();
// Now that we're done with our recursive CanTransmit checks, flag this relay as not checked so it can be
// used next time.
RelayDatabase.Instance.CheckedVesselsTable.Remove(vessel.id);
-
- // Return the nearest available relay, or null if there are no available relays nearby.
- return _nearestRelay;
- }
-
- /// <summary>
- /// Initializes a new instance of the <see cref="AntennaRange.ProtoDataTransmitter"/> class.
- /// </summary>
- /// <param name="ms"><see cref="ProtoPartModuleSnapshot"/></param>
+ }
+
+ /// <summary>
+ /// Returns a <see cref="System.String"/> that represents the current <see cref="AntennaRange.AntennaRelay"/>.
+ /// </summary>
+ /// <returns>A <see cref="System.String"/> that represents the current <see cref="AntennaRange.AntennaRelay"/>.</returns>
+ public override string ToString()
+ {
+ if (this is ProtoAntennaRelay)
+ {
+ return (this as ProtoAntennaRelay).ToString();
+ }
+ return this.moduleRef.ToString();
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="AntennaRange.AntennaRelay"/> class.
+ /// </summary>
+ /// <param name="module">The module reference underlying this AntennaRelay,
+ /// as an <see cref="AntennaRange.IAntennaRelay"/></param>
public AntennaRelay(IAntennaRelay module)
{
this.moduleRef = module;
this.searchTimer = new System.Diagnostics.Stopwatch();
- this.millisecondsBetweenSearches = 5000;
+ this.millisecondsBetweenSearches = 125L;
}
}
}
--- a/IAntennaRelay.cs
+++ b/IAntennaRelay.cs
@@ -31,41 +31,40 @@
namespace AntennaRange
{
- /*
- * Interface defining the basic functionality of AntennaRelay modules for AntennaRange.
- * */
+ /// <summary>
+ /// Interface defining the basic functionality of AntennaRelay modules for AntennaRange.
+ /// </summary>
public interface IAntennaRelay
{
/// <summary>
/// Gets the parent Vessel.
/// </summary>
- /// <value>The parent Vessel.</value>
Vessel vessel { get; }
+
+ /// <summary>
+ /// Gets the target <see cref="AntennaRange.IAntennaRelay"/>relay.
+ /// </summary>
+ IAntennaRelay targetRelay { get; }
/// <summary>
/// Gets the distance to the nearest relay or Kerbin, whichever is closer.
/// </summary>
- /// <value>The distance to the nearest relay or Kerbin, whichever is closer.</value>
double transmitDistance { get; }
+
+ /// <summary>
+ /// Gets the nominal transmit distance at which the Antenna behaves just as prescribed by Squad's config.
+ /// </summary>
+ double nominalTransmitDistance { get; }
/// <summary>
/// The maximum distance at which this relay can operate.
/// </summary>
- /// <value>The max transmit distance.</value>
- float maxTransmitDistance { get; }
+ double maxTransmitDistance { get; }
/// <summary>
/// The first CelestialBody blocking line of sight to a
/// </summary>
- /// <value>The first occluding body.</value>
CelestialBody firstOccludingBody { get; }
-
- /// <summary>
- /// Gets a value indicating whether this <see cref="AntennaRange.ProtoDataTransmitter"/> has been checked during
- /// the current relay attempt.
- /// </summary>
- /// <value><c>true</c> if relay checked; otherwise, <c>false</c>.</value>
- bool relayChecked { get; }
/// <summary>
/// Gets a value indicating whether this <see cref="AntennaRange.IAntennaRelay"/> Relay is communicating
@@ -74,10 +73,20 @@
bool KerbinDirect { get; }
/// <summary>
+ /// Gets the Part title.
+ /// </summary>
+ string Title { get; }
+
+ /// <summary>
/// Determines whether this instance can transmit.
+ /// <c>true</c> if this instance can transmit; otherwise, <c>false</c>.
/// </summary>
- /// <returns><c>true</c> if this instance can transmit; otherwise, <c>false</c>.</returns>
bool CanTransmit();
+
+ /// <summary>
+ /// Returns a <see cref="System.String"/> that represents the current <see cref="AntennaRange.IAntennaRelay"/>.
+ /// </summary>
+ string ToString();
}
}
--- a/ModuleLimitedDataTransmitter.cs
+++ b/ModuleLimitedDataTransmitter.cs
@@ -36,71 +36,90 @@
namespace AntennaRange
{
- /*
- * ModuleLimitedDataTransmitter is designed as a drop-in replacement for ModuleDataTransmitter, and handles range-
- * finding, power scaling, and data scaling for antennas during science transmission. Its functionality varies with
- * three tunables: nominalRange, maxPowerFactor, and maxDataFactor, set in .cfg files.
- *
- * In general, the scaling functions assume the following relation:
- *
- * D² α P/R,
- *
- * where D is the total transmission distance, P is the transmission power, and R is the data rate.
- *
- * */
-
- /*
- * Fields
- * */
+ /// <summary>
+ /// <para>ModuleLimitedDataTransmitter is designed as a drop-in replacement for ModuleDataTransmitter, and handles
+ /// rangefinding, power scaling, and data scaling for antennas during science transmission. Its functionality
+ /// varies with three tunables: nominalRange, maxPowerFactor, and maxDataFactor, set in .cfg files.</para>
+ ///
+ /// <para>In general, the scaling functions assume the following relation:</para>
+ ///
+ /// <para> D² α P/R,</para>
+ ///
+ /// <para>where D is the total transmission distance, P is the transmission power, and R is the data rate.</para>
+ /// </summary>
public class ModuleLimitedDataTransmitter : ModuleDataTransmitter, IScienceDataTransmitter, IAntennaRelay
{
// Stores the packetResourceCost as defined in the .cfg file.
- protected float _basepacketResourceCost;
+ private float _basepacketResourceCost;
// Stores the packetSize as defined in the .cfg file.
- protected float _basepacketSize;
+ private float _basepacketSize;
// Every antenna is a relay.
- protected AntennaRelay relay;
-
- // Keep track of vessels with transmitters for relay purposes.
- protected List<Vessel> _relayVessels;
+ private AntennaRelay relay;
// Sometimes we will need to communicate errors; this is how we do it.
- protected ScreenMessage ErrorMsg;
-
- // The distance from Kerbin at which the antenna will perform exactly as prescribed by packetResourceCost
- // and packetSize.
+ private ScreenMessage ErrorMsg;
+
+ /// <summary>
+ /// The distance from Kerbin at which the antenna will perform exactly as prescribed by packetResourceCost
+ /// and packetSize.
+ /// </summary>
[KSPField(isPersistant = false)]
- public float nominalRange;
-
+ public double nominalRange;
+
+ /// <summary>
+ /// Relay status string for use in action menus.
+ /// </summary>
[KSPField(isPersistant = false, guiActive = true, guiName = "Status")]
public string UIrelayStatus;
+ /// <summary>
+ /// Relay target string for use in action menus.
+ /// </summary>
[KSPField(isPersistant = false, guiActive = true, guiName = "Relay")]
public string UIrelayTarget;
+ /// <summary>
+ /// Transmit distance string for use in action menus.
+ /// </summary>
[KSPField(isPersistant = false, guiActive = true, guiName = "Transmission Distance")]
public string UItransmitDistance;
+ /// <summary>
+ /// Maximum distance string for use in action menus.
+ /// </summary>
[KSPField(isPersistant = false, guiActive = true, guiName = "Maximum Distance")]
public string UImaxTransmitDistance;
+ /// <summary>
+ /// Packet size string for use in action menus.
+ /// </summary>
[KSPField(isPersistant = false, guiActive = true, guiName = "Packet Size")]
public string UIpacketSize;
+ /// <summary>
+ /// Packet cost string for use in action menus.
+ /// </summary>
[KSPField(isPersistant = false, guiActive = true, guiName = "Packet Cost")]
public string UIpacketCost;
- // The multiplier on packetResourceCost that defines the maximum power output of the antenna. When the power
- // cost exceeds packetResourceCost * maxPowerFactor, transmission will fail.
+ /// <summary>
+ /// The multiplier on packetResourceCost that defines the maximum power output of the antenna. When the power
+ /// cost exceeds packetResourceCost * maxPowerFactor, transmission will fail.
+ /// </summary>
[KSPField(isPersistant = false)]
public float maxPowerFactor;
- // The multipler on packetSize that defines the maximum data bandwidth of the antenna.
+ /// <summary>
+ /// The multipler on packetSize that defines the maximum data bandwidth of the antenna.
+ /// </summary>
[KSPField(isPersistant = false)]
public float maxDataFactor;
+ /// <summary>
+ /// The packet throttle.
+ /// </summary>
[KSPField(
isPersistant = true,
guiName = "Packet Throttle",
@@ -111,38 +130,92 @@
[UI_FloatRange(maxValue = 100f, minValue = 2.5f, stepIncrement = 2.5f)]
public float packetThrottle;
- protected bool actionUIUpdate;
+ private bool actionUIUpdate;
/*
* Properties
* */
- // Returns the parent vessel housing this antenna.
+ /// <summary>
+ /// Gets the parent Vessel.
+ /// </summary>
public new Vessel vessel
{
get
{
- return base.vessel;
- }
- }
-
- // Returns the distance to the nearest relay or Kerbin, whichever is closer.
+ if (base.vessel != null)
+ {
+ return base.vessel;
+ }
+ else if (this.part != null)
+ {
+ return this.part.vessel;
+ }
+
+ else
+ {
+ return null;
+ }
+ }
+ }
+
+ /// <summary>
+ /// Gets the target <see cref="AntennaRange.IAntennaRelay"/>relay.
+ /// </summary>
+ public IAntennaRelay targetRelay
+ {
+ get
+ {
+ if (this.relay == null)
+ {
+ return null;
+ }
+
+ return this.relay.targetRelay;
+ }
+ }
+
+ /// <summary>
+ /// Gets the distance to the nearest relay or Kerbin, whichever is closer.
+ /// </summary>
public double transmitDistance
{
get
{
+ if (this.relay == null)
+ {
+ return double.PositiveInfinity;
+ }
+
return this.relay.transmitDistance;
}
}
- // Returns the maximum distance this module can transmit
- public float maxTransmitDistance
- {
- get
- {
- return Mathf.Sqrt (this.maxPowerFactor) * this.nominalRange;
- }
- }
-
+ /// <summary>
+ /// Gets the nominal transmit distance at which the Antenna behaves just as prescribed by Squad's config.
+ /// </summary>
+ public double nominalTransmitDistance
+ {
+ get
+ {
+ return this.nominalRange;
+ }
+ }
+
+ /// <summary>
+ /// The maximum distance at which this relay can operate.
+ /// </summary>
+ public double maxTransmitDistance
+ {
+ get
+ {
+ // TODO: Cache this in a way that doesn't break everything.
+ return Math.Sqrt(this.maxPowerFactor) * this.nominalRange;
+ }
+ }
+
+ /// <summary>
+ /// The first CelestialBody blocking line of sight to a
+ /// </summary>
public CelestialBody firstOccludingBody
{
get
@@ -177,8 +250,10 @@
*
* So... hopefully that doesn't screw with anything else.
* */
- // Override ModuleDataTransmitter.DataRate to just return packetSize, because we want antennas to be scored in
- // terms of joules/byte
+ /// <summary>
+ /// Override ModuleDataTransmitter.DataRate to just return packetSize, because we want antennas to be scored in
+ /// terms of joules/byte
+ /// </summary>
public new float DataRate
{
get
@@ -196,9 +271,11 @@
}
}
- // Override ModuleDataTransmitter.DataResourceCost to just return packetResourceCost, because we want antennas
- // to be scored in terms of joules/byte
- public new float DataResourceCost
+ /// <summary>
+ /// Override ModuleDataTransmitter.DataResourceCost to just return packetResourceCost, because we want antennas
+ /// to be scored in terms of joules/byte
+ /// </summary>
+ public new double DataResourceCost
{
get
{
@@ -215,31 +292,36 @@
}
}
- // Reports whether this antenna has been checked as a viable relay already in the current FindNearestRelay.
- public bool relayChecked
+ /// <summary>
+ /// Gets a value indicating whether this <see cref="AntennaRange.IAntennaRelay"/> Relay is communicating
+ /// directly with Kerbin.
+ /// </summary>
+ public bool KerbinDirect
{
get
{
if (this.relay != null)
{
- return this.relay.relayChecked;
- }
-
- // If our relay is null, always return null so we're never checked.
- return true;
- }
- }
-
- public bool KerbinDirect
- {
- get
- {
- if (this.relay != null)
- {
return this.relay.KerbinDirect;
}
return false;
+ }
+ }
+
+ /// <summary>
+ /// Gets the Part title.
+ /// </summary>
+ public string Title
+ {
+ get
+ {
+ if (this.part != null && this.part.partInfo != null)
+ {
+ return this.part.partInfo.title;
+ }
+
+ return string.Empty;
}
}
@@ -253,6 +335,9 @@
this.packetThrottle = 100f;
}
+ /// <summary>
+ /// PartModule OnAwake override; runs at Unity Awake.
+ /// </summary>
public override void OnAwake()
{
base.OnAwake();
@@ -276,7 +361,10 @@
));
}
- // At least once, when the module starts with a state on the launch pad or later, go find Kerbin.
+ /// <summary>
+ /// PartModule OnStart override; runs at Unity Start.
+ /// </summary>
+ /// <param name="state">State.</param>
public override void OnStart (StartState state)
{
base.OnStart (state);
@@ -285,6 +373,7 @@
{
this.relay = new AntennaRelay(this);
this.relay.maxTransmitDistance = this.maxTransmitDistance;
+ this.relay.nominalTransmitDistance = this.nominalRange;
this.UImaxTransmitDistance = Tools.MuMech_ToSI(this.maxTransmitDistance) + "m";
@@ -293,9 +382,12 @@
}
}
- // When the module loads, fetch the Squad KSPFields from the base. This is necessary in part because
- // overloading packetSize and packetResourceCostinto a property in ModuleLimitedDataTransmitter didn't
- // work.
+ /// <summary>
+ /// When the module loads, fetch the Squad KSPFields from the base. This is necessary in part because
+ /// overloading packetSize and packetResourceCostinto a property in ModuleLimitedDataTransmitter didn't
+ /// work.
+ /// </summary>
+ /// <param name="node"><see cref="ConfigNode"/> with data for this module.</param>
public override void OnLoad(ConfigNode node)
{
this.Fields.Load(node);
@@ -304,81 +396,9 @@
base.OnLoad (node);
}
- // Post an error in the communication messages describing the reason transmission has failed. Currently there
- // is only one reason for this.
- protected void PostCannotTransmitError()
- {
- string ErrorText = string.Intern("Unable to transmit: no visible receivers in range!");
-
- this.ErrorMsg.message = string.Format(
- "<color='#{0}{1}{2}{3}'><b>{4}</b></color>",
- ((int)(XKCDColors.OrangeRed.r * 255f)).ToString("x2"),
- ((int)(XKCDColors.OrangeRed.g * 255f)).ToString("x2"),
- ((int)(XKCDColors.OrangeRed.b * 255f)).ToString("x2"),
- ((int)(XKCDColors.OrangeRed.a * 255f)).ToString("x2"),
- ErrorText
- );
-
- Tools.PostDebugMessage(this.GetType().Name + ": " + this.ErrorMsg.message);
-
- ScreenMessages.PostScreenMessage(this.ErrorMsg, false);
- }
-
- // Before transmission, set packetResourceCost. Per above, packet cost increases with the square of
- // distance. packetResourceCost maxes out at _basepacketResourceCost * maxPowerFactor, at which point
- // transmission fails (see CanTransmit).
- protected void PreTransmit_SetPacketResourceCost()
- {
- if (ARConfiguration.FixedPowerCost || this.transmitDistance <= this.nominalRange)
- {
- base.packetResourceCost = this._basepacketResourceCost;
- }
- else
- {
- double rangeFactor = (this.transmitDistance / this.nominalRange);
- rangeFactor *= rangeFactor;
-
- base.packetResourceCost = this._basepacketResourceCost
- * (float)rangeFactor;
-
- Tools.PostDebugMessage(
- this,
- "Pretransmit: packet cost set to {0} before throttle (rangeFactor = {1}).",
- base.packetResourceCost,
- rangeFactor);
- }
-
- base.packetResourceCost *= this.packetThrottle / 100f;
- }
-
- // Before transmission, set packetSize. Per above, packet size increases with the inverse square of
- // distance. packetSize maxes out at _basepacketSize * maxDataFactor.
- protected void PreTransmit_SetPacketSize()
- {
- if (!ARConfiguration.FixedPowerCost && this.transmitDistance >= this.nominalRange)
- {
- base.packetSize = this._basepacketSize;
- }
- else
- {
- double rangeFactor = (this.nominalRange / this.transmitDistance);
- rangeFactor *= rangeFactor;
-
- base.packetSize = Math.Min(
- this._basepacketSize * (float)rangeFactor,
- this._basepacketSize * this.maxDataFactor);
-
- Tools.PostDebugMessage(
- this,
- "Pretransmit: packet size set to {0} before throttle (rangeFactor = {1}).",
- base.packetSize,
- rangeFactor);
- }
-
- base.packetSize *= this.packetThrottle / 100f;
- }
-
- // Override ModuleDataTransmitter.GetInfo to add nominal and maximum range to the VAB description.
+ /// <summary>
+ /// Override ModuleDataTransmitter.GetInfo to add nominal and maximum range to the VAB description.
+ /// </summary>
public override string GetInfo()
{
string text = base.GetInfo();
@@ -387,7 +407,10 @@
return text;
}
- // Override ModuleDataTransmitter.CanTransmit to return false when transmission is not possible.
+ /// <summary>
+ /// Determines whether this instance can transmit.
+ /// <c>true</c> if this instance can transmit; otherwise, <c>false</c>.
+ /// </summary>
public new bool CanTransmit()
{
if (this.part == null || this.relay == null)
@@ -395,53 +418,41 @@
return false;
}
- PartStates partState = this.part.State;
- if (partState == PartStates.DEAD || partState == PartStates.DEACTIVATED)
- {
- Tools.PostDebugMessage(string.Format(
- "{0}: {1} on {2} cannot transmit: {3}",
- this.GetType().Name,
- this.part.partInfo.title,
- this.vessel.vesselName,
- Enum.GetName(typeof(PartStates), partState)
- ));
- return false;
- }
+ switch (this.part.State)
+ {
+ case PartStates.DEAD:
+ case PartStates.DEACTIVATED:
+ Tools.PostDebugMessage(string.Format(
+ "{0}: {1} on {2} cannot transmit: {3}",
+ this.GetType().Name,
+ this.part.partInfo.title,
+ this.vessel.vesselName,
+ Enum.GetName(typeof(PartStates), this.part.State)
+ ));
+ return false;
+ default:
+ break;
+ }
+
return this.relay.CanTransmit();
}
- // Override ModuleDataTransmitter.TransmitData to check against CanTransmit and fail out when CanTransmit
- // returns false.
- public new void TransmitData(List<ScienceData> dataQueue)
+ /// <summary>
+ /// Override ModuleDataTransmitter.TransmitData to check against CanTransmit and fail out when CanTransmit
+ /// returns false.
+ /// </summary>
+ /// <param name="dataQueue">List of <see cref="ScienceData"/> to transmit.</param>
+ /// <param name="callback">Callback function</param>
+ public new void TransmitData(List<ScienceData> dataQueue, Callback callback)
{
this.PreTransmit_SetPacketSize();
this.PreTransmit_SetPacketResourceCost();
if (this.CanTransmit())
{
- StringBuilder message = new StringBuilder();
-
- message.Append("[");
- message.Append(base.part.partInfo.title);
- message.Append("]: ");
-
- message.Append("Beginning transmission ");
-
- // @DONE TODO: Fix this to fall back to Kerbin if nearestRelay cannot be contacted.
- // @DONE TODO: Remove nearestRelay == null
- if (this.KerbinDirect)
- {
- message.Append("directly to Kerbin.");
- }
- else
- {
- message.Append("via ");
- message.Append(this.relay.nearestRelay);
- }
-
- ScreenMessages.PostScreenMessage(message.ToString(), 4f, ScreenMessageStyle.UPPER_LEFT);
-
- base.TransmitData(dataQueue);
+ ScreenMessages.PostScreenMessage(this.buildTransmitMessage(), 4f, ScreenMessageStyle.UPPER_LEFT);
+
+ base.TransmitData(dataQueue, callback);
}
else
{
@@ -506,7 +517,7 @@
foreach (ScienceData data in dataQueue)
{
- msg.AppendFormat("\n{0}\n", data.title);
+ msg.AppendFormat("\t{0}\n", data.title);
}
ScreenMessages.PostScreenMessage(msg.ToString(), 4f, ScreenMessageStyle.UPPER_LEFT);
@@ -514,7 +525,7 @@
Tools.PostDebugMessage(msg.ToString());
}
- this.PostCannotTransmitError ();
+ this.PostCannotTransmitError();
}
Tools.PostDebugMessage (
@@ -524,8 +535,20 @@
);
}
- // Override ModuleDataTransmitter.StartTransmission to check against CanTransmit and fail out when CanTransmit
- // returns false.
+ /// <summary>
+ /// Override ModuleDataTransmitter.TransmitData to check against CanTransmit and fail out when CanTransmit
+ /// returns false.
+ /// </summary>
+ /// <param name="dataQueue">List of <see cref="ScienceData"/> to transmit.</param>
+ public new void TransmitData(List<ScienceData> dataQueue)
+ {
+ this.TransmitData(dataQueue, null);
+ }
+
+ /// <summary>
+ /// Override ModuleDataTransmitter.StartTransmission to check against CanTransmit and fail out when CanTransmit
+ /// returns false.
+ /// </summary>
public new void StartTransmission()
{
PreTransmit_SetPacketSize ();
@@ -539,27 +562,7 @@
if (this.CanTransmit())
{
- StringBuilder message = new StringBuilder();
-
- message.Append("[");
- message.Append(base.part.partInfo.title);
- message.Append("]: ");
-
- message.Append("Beginning transmission ");
-
- // @DONE TODO: Fix this to fall back to Kerbin if nearestRelay cannot be contacted.
- // @DONE TODO: Remove nearestRelay == null
- if (this.KerbinDirect)
- {
- message.Append("directly to Kerbin.");
- }
- else
- {
- message.Append("via ");
- message.Append(this.relay.nearestRelay);
- }
-
- ScreenMessages.PostScreenMessage(message.ToString(), 4f, ScreenMessageStyle.UPPER_LEFT);
+ ScreenMessages.PostScreenMessage(this.buildTransmitMessage(), 4f, ScreenMessageStyle.UPPER_LEFT);
base.StartTransmission();
}
@@ -569,6 +572,9 @@
}
}
+ /// <summary>
+ /// MonoBehaviour Update
+ /// </summary>
public void Update()
{
if (this.actionUIUpdate)
@@ -597,31 +603,19 @@
if (this.KerbinDirect)
{
- this.UIrelayTarget = "Kerbin";
+ this.UIrelayTarget = AntennaRelay.Kerbin.bodyName;
}
else
{
- this.UIrelayTarget = this.relay.nearestRelay.ToString();
- }
- }
- }
-
- public void onPartActionUICreate(Part eventPart)
- {
- if (eventPart == base.part)
- {
- this.actionUIUpdate = true;
- }
- }
-
- public void onPartActionUIDismiss(Part eventPart)
- {
- if (eventPart == base.part)
- {
- this.actionUIUpdate = false;
- }
- }
-
+ this.UIrelayTarget = this.targetRelay.ToString();
+ }
+ }
+ }
+
+ /// <summary>
+ /// Returns a <see cref="System.String"/> that represents the current <see cref="AntennaRange.ModuleLimitedDataTransmitter"/>.
+ /// </summary>
+ /// <returns>A <see cref="System.String"/> that represents the current <see cref="AntennaRange.ModuleLimitedDataTransmitter"/>.</returns>
public override string ToString()
{
StringBuilder msg = new StringBuilder();
@@ -633,12 +627,137 @@
msg.Append(" on ");
msg.Append(vessel.vesselName);
}
+ else if (
+ this.part != null &&
+ this.part.protoPartSnapshot != null &&
+ this.part.protoPartSnapshot != null &&
+ this.part.protoPartSnapshot.pVesselRef != null
+ )
+ {
+ msg.Append(" on ");
+ msg.Append(this.part.protoPartSnapshot.pVesselRef.vesselName);
+ }
return msg.ToString();
}
+ // When we catch an onPartActionUICreate event for our part, go ahead and update every frame to look pretty.
+ private void onPartActionUICreate(Part eventPart)
+ {
+ if (eventPart == base.part)
+ {
+ this.actionUIUpdate = true;
+ }
+ }
+
+ // When we catch an onPartActionUIDismiss event for our part, stop updating every frame to look pretty.
+ private void onPartActionUIDismiss(Part eventPart)
+ {
+ if (eventPart == base.part)
+ {
+ this.actionUIUpdate = false;
+ }
+ }
+
+ // Post an error in the communication messages describing the reason transmission has failed. Currently there
+ // is only one reason for this.
+ private void PostCannotTransmitError()
+ {
+ string ErrorText = string.Intern("Unable to transmit: no visible receivers in range!");
+
+ this.ErrorMsg.message = string.Format(
+ "<color='#{0}{1}{2}{3}'><b>{4}</b></color>",
+ ((int)(XKCDColors.OrangeRed.r * 255f)).ToString("x2"),
+ ((int)(XKCDColors.OrangeRed.g * 255f)).ToString("x2"),
+ ((int)(XKCDColors.OrangeRed.b * 255f)).ToString("x2"),
+ ((int)(XKCDColors.OrangeRed.a * 255f)).ToString("x2"),
+ ErrorText
+ );
+
+ Tools.PostDebugMessage(this.GetType().Name + ": " + this.ErrorMsg.message);
+
+ ScreenMessages.PostScreenMessage(this.ErrorMsg, false);
+ }
+
+ // Before transmission, set packetResourceCost. Per above, packet cost increases with the square of
+ // distance. packetResourceCost maxes out at _basepacketResourceCost * maxPowerFactor, at which point
+ // transmission fails (see CanTransmit).
+ private void PreTransmit_SetPacketResourceCost()
+ {
+ if (ARConfiguration.FixedPowerCost || this.transmitDistance <= this.nominalRange)
+ {
+ base.packetResourceCost = this._basepacketResourceCost;
+ }
+ else
+ {
+ float rangeFactor = (float)(this.transmitDistance / this.nominalRange);
+ rangeFactor *= rangeFactor;
+
+ base.packetResourceCost = this._basepacketResourceCost
+ * rangeFactor;
+
+ Tools.PostDebugMessage(
+ this,
+ "Pretransmit: packet cost set to {0} before throttle (rangeFactor = {1}).",
+ base.packetResourceCost,
+ rangeFactor);
+ }
+
+ base.packetResourceCost *= this.packetThrottle / 100f;
+ }
+
+ // Before transmission, set packetSize. Per above, packet size increases with the inverse square of
+ // distance. packetSize maxes out at _basepacketSize * maxDataFactor.
+ private void PreTransmit_SetPacketSize()
+ {
+ if (!ARConfiguration.FixedPowerCost && this.transmitDistance >= this.nominalRange)
+ {
+ base.packetSize = this._basepacketSize;
+ }
+ else
+ {
+ float rangeFactor = (float)(this.nominalRange / this.transmitDistance);
+ rangeFactor *= rangeFactor;
+
+ base.packetSize = Mathf.Min(
+ this._basepacketSize * rangeFactor,
+ this._basepacketSize * this.maxDataFactor);
+
+ Tools.PostDebugMessage(
+ this,
+ "Pretransmit: packet size set to {0} before throttle (rangeFactor = {1}).",
+ base.packetSize,
+ rangeFactor);
+ }
+
+ base.packetSize *= this.packetThrottle / 100f;
+ }
+
+ private string buildTransmitMessage()
+ {
+ StringBuilder message = new StringBuilder();
+
+ message.Append("[");
+ message.Append(base.part.partInfo.title);
+ message.Append("]: ");
+
+ message.Append("Beginning transmission ");
+
+ if (this.KerbinDirect)
+ {
+ message.Append("directly to Kerbin.");
+ }
+ else
+ {
+ message.Append("via ");
+ message.Append(this.relay.targetRelay);
+ }
+
+ return message.ToString();
+ }
+
+ #if DEBUG
// When debugging, it's nice to have a button that just tells you everything.
- #if DEBUG
[KSPEvent (guiName = "Show Debug Info", active = true, guiActive = true)]
public void DebugInfo()
{
@@ -658,8 +777,9 @@
"DataRate: {9}\n" +
"DataResourceCost: {10}\n" +
"TransmitterScore: {11}\n" +
- "NearestRelay: {12}\n" +
- "Vessel ID: {13}",
+ "targetRelay: {12}\n" +
+ "KerbinDirect: {13}\n" +
+ "Vessel ID: {14}",
this.name,
this._basepacketSize,
base.packetSize,
@@ -672,10 +792,12 @@
this.DataRate,
this.DataResourceCost,
ScienceUtil.GetTransmitterScore(this),
- this.relay.FindNearestRelay(),
+ this.relay.targetRelay == null ? "null" : this.relay.targetRelay.ToString(),
+ this.KerbinDirect,
this.vessel.id
);
- Tools.PostDebugMessage(msg);
+
+ Tools.PostLogMessage(msg);
}
[KSPEvent (guiName = "Dump Vessels", active = true, guiActive = true)]
@@ -692,7 +814,7 @@
Tools.PostDebugMessage(sb.ToString());
}
-
+
[KSPEvent (guiName = "Dump RelayDB", active = true, guiActive = true)]
public void DumpRelayDB()
{
--- a/ProtoAntennaRelay.cs
+++ b/ProtoAntennaRelay.cs
@@ -40,21 +40,41 @@
public class ProtoAntennaRelay : AntennaRelay, IAntennaRelay
{
// Stores the prototype part so we can make sure we haven't exploded or so.
- protected ProtoPartSnapshot protoPart;
+ private ProtoPartSnapshot protoPart;
+ /// <summary>
+ /// Gets the parent Vessel.
+ /// </summary>
public override Vessel vessel
{
get
{
- return this.protoPart.pVesselRef.vesselRef;
+ if (this.protoPart != null && this.protoPart.pVesselRef != null)
+ {
+ return this.protoPart.pVesselRef.vesselRef;
+ }
+ else
+ {
+ return null;
+ }
}
}
/// <summary>
- /// The maximum distance at which this transmitter can operate.
+ /// Gets the nominal transmit distance at which the Antenna behaves just as prescribed by Squad's config.
/// </summary>
- /// <value>The max transmit distance.</value>
- public override float maxTransmitDistance
+ public override double nominalTransmitDistance
+ {
+ get
+ {
+ return this.moduleRef.nominalTransmitDistance;
+ }
+ }
+
+ /// <summary>
+ /// The maximum distance at which this relay can operate.
+ /// </summary>
+ public override double maxTransmitDistance
{
get
{
@@ -63,28 +83,26 @@
}
/// <summary>
- /// Gets a value indicating whether this <see cref="AntennaRange.ProtoDataTransmitter"/> has been checked during
- /// the current relay attempt.
+ /// Gets the underlying part's title.
/// </summary>
- /// <value><c>true</c> if relay checked; otherwise, <c>false</c>.</value>
- public override bool relayChecked
+ /// <value>The title.</value>
+ public string Title
{
- get;
- protected set;
+ get
+ {
+ if (this.protoPart != null && this.protoPart.partInfo != null)
+ {
+ return this.protoPart.partInfo.title;
+ }
+
+ return string.Empty;
+ }
}
/// <summary>
- /// Gets the underlying part's title.
+ /// Determines whether this instance can transmit.
+ /// <c>true</c> if this instance can transmit; otherwise, <c>false</c>.
/// </summary>
- /// <value>The title.</value>
- public string title
- {
- get
- {
- return this.protoPart.partInfo.title;
- }
- }
-
public override bool CanTransmit()
{
PartStates partState = (PartStates)this.protoPart.state;
@@ -93,7 +111,7 @@
Tools.PostDebugMessage(string.Format(
"{0}: {1} on {2} cannot transmit: {3}",
this.GetType().Name,
- this.title,
+ this.Title,
this.vessel.vesselName,
Enum.GetName(typeof(PartStates), partState)
));
@@ -102,11 +120,15 @@
return base.CanTransmit();
}
+ /// <summary>
+ /// Returns a <see cref="System.String"/> that represents the current <see cref="AntennaRange.ProtoAntennaRelay"/>.
+ /// </summary>
+ /// <returns>A <see cref="System.String"/> that represents the current <see cref="AntennaRange.ProtoAntennaRelay"/>.</returns>
public override string ToString()
{
return string.Format(
"{0} on {1}",
- this.title,
+ this.Title,
this.protoPart.pVesselRef.vesselName
);
}
--- a/RelayExtensions.cs
+++ b/RelayExtensions.cs
@@ -78,7 +78,7 @@
return relay.vessel.sqrDistanceTo(body);
}
- public static double sqrDistanceTo(this AntennaRelay relayOne, AntennaRelay relayTwo)
+ public static double sqrDistanceTo(this AntennaRelay relayOne, IAntennaRelay relayTwo)
{
return relayOne.vessel.sqrDistanceTo(relayTwo.vessel);
}
@@ -89,7 +89,7 @@
/// <param name="vessel">This <see cref="Vessel"/></param>
public static IEnumerable<IAntennaRelay> GetAntennaRelays (this Vessel vessel)
{
- return RelayDatabase.Instance[vessel].Values.ToList();
+ return RelayDatabase.Instance[vessel].Values.ToList().AsReadOnly();
}
/// <summary>
@@ -109,6 +109,59 @@
return false;
}
+
+ public static ConnectionStatus GetConnectionStatus(this Vessel vessel)
+ {
+ bool canTransmit = false;
+
+ foreach (IAntennaRelay relay in RelayDatabase.Instance[vessel].Values)
+ {
+ if (relay.CanTransmit())
+ {
+ canTransmit = true;
+ if (relay.transmitDistance <= relay.nominalTransmitDistance)
+ {
+ return ConnectionStatus.Optimal;
+ }
+ }
+ }
+
+ if (canTransmit)
+ {
+ return ConnectionStatus.Suboptimal;
+ }
+ else
+ {
+ return ConnectionStatus.None;
+ }
+ }
+
+ public static IAntennaRelay GetBestRelay(this Vessel vessel)
+ {
+ IAntennaRelay bestRelay = null;
+ double bestScore = double.PositiveInfinity;
+ double relayScore = double.NaN;
+
+ foreach (IAntennaRelay relay in vessel.GetAntennaRelays())
+ {
+ relayScore = relay.transmitDistance / relay.maxTransmitDistance;
+
+ if (relayScore < bestScore)
+ {
+ bestScore = relayScore;
+ bestRelay = relay;
+ }
+ }
+
+ return bestRelay;
+ }
+ }
+
+ public enum ConnectionStatus
+ {
+ None,
+ Suboptimal,
+ Optimal
}
}