2
0
mirror of https://github.com/9ParsonsB/Pulsar.git synced 2025-04-05 17:39:39 -04:00
F K 35ab70b743
[Explorer] Default + Custom Criteria improvements (#130)
* [Explorer] Default + Custom Criteria improvements

For Custom Criteria:
* If a custom criteria throws an error or fails to parse/load, Core no longer disables custom criteria entirely -- it now disables the problematic criteria until you update the custom criteria file and trigger a new journal/re-read (which reloads the source file).

For Default Criteria:
* Adds landable status of the body triggering the Close Ring/Belt proximity. (Requested in Discord somewhere.)
* Adds the triggering ring name to the Wide Ring criteria. (I believe also requested, can't remember now.)
2024-01-25 22:50:59 -05:00

322 lines
12 KiB
C#

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.IO;
using System.Linq;
using System.Text;
using Observatory.Framework;
using Observatory.Framework.Files.Journal;
using Observatory.Framework.Interfaces;
namespace Observatory.Explorer
{
internal class Explorer
{
private IObservatoryCore ObservatoryCore;
private ObservableCollection<object> Results;
private ExplorerWorker ExplorerWorker;
private Dictionary<ulong, Dictionary<int, Scan>> SystemBodyHistory;
private Dictionary<ulong, Dictionary<int, FSSBodySignals>> BodySignalHistory;
private Dictionary<ulong, Dictionary<int, ScanBaryCentre>> BarycentreHistory;
private CustomCriteriaManager CustomCriteriaManager;
private DateTime CriteriaLastModified;
private string currentSystem = string.Empty;
internal Explorer(ExplorerWorker explorerWorker, IObservatoryCore core, ObservableCollection<object> results)
{
SystemBodyHistory = new();
BodySignalHistory = new();
BarycentreHistory = new();
ExplorerWorker = explorerWorker;
ObservatoryCore = core;
Results = results;
CustomCriteriaManager = new(core.GetPluginErrorLogger(explorerWorker));
CriteriaLastModified = new DateTime(0);
}
public void Clear()
{
SystemBodyHistory.Clear();
BodySignalHistory.Clear();
BarycentreHistory.Clear();
}
public void RecordSignal(FSSBodySignals bodySignals)
{
if (!BodySignalHistory.ContainsKey(bodySignals.SystemAddress))
{
BodySignalHistory.Add(bodySignals.SystemAddress, new Dictionary<int, FSSBodySignals>());
}
if (!BodySignalHistory[bodySignals.SystemAddress].ContainsKey(bodySignals.BodyID))
{
BodySignalHistory[bodySignals.SystemAddress].Add(bodySignals.BodyID, bodySignals);
}
}
public void RecordBarycentre(ScanBaryCentre scan)
{
if (!BarycentreHistory.ContainsKey(scan.SystemAddress))
{
BarycentreHistory.Add(scan.SystemAddress, new Dictionary<int, ScanBaryCentre>());
}
if (!BarycentreHistory[scan.SystemAddress].ContainsKey(scan.BodyID))
{
BarycentreHistory[scan.SystemAddress].Add(scan.BodyID, scan);
}
}
private static string IncrementOrdinal(string ordinal)
{
char ordChar = ordinal.ToCharArray().Last();
if (new char[] {'Z', '9'}.Contains(ordChar))
{
ordinal = IncrementOrdinal(ordinal.Length == 1 ? " " : String.Empty + ordinal[..^1]);
ordChar = (char)(ordChar - 10);
}
return ordinal[..^1] + (char)(ordChar + 1);
}
private static string DecrementOrdinal(string ordinal)
{
char ordChar = ordinal.ToCharArray().Last();
if (new char[] { 'A', '0' }.Contains(ordChar))
{
ordinal = DecrementOrdinal(ordinal.Length == 1 ? " " : String.Empty + ordinal[..^1]);
ordChar = (char)(ordChar + 10);
}
return ordinal[..^1] + (char)(ordChar - 1);
}
public Scan ConvertBarycentre(ScanBaryCentre barycentre, Scan childScan)
{
string childAffix = childScan.BodyName
.Replace(childScan.StarSystem, string.Empty).Trim();
string baryName;
if (!string.IsNullOrEmpty(childAffix))
{
char childOrdinal = childAffix.ToCharArray().Last();
// If the ID is one higher than the barycentre than this is the "first" child body
bool lowChild = childScan.BodyID - barycentre.BodyID == 1;
string baryAffix;
// Barycentre ordinal always labelled as low before high, e.g. "AB"
if (lowChild)
{
baryAffix = childAffix + "-" + IncrementOrdinal(childAffix);
}
else
{
baryAffix = DecrementOrdinal(childAffix) + "-" + childAffix;
}
baryName = barycentre.StarSystem + " " + baryAffix;
}
else
{
// Without ordinals it's complicated to determine what the ordinal *should* be.
// Just name the barycentre after the child object.
baryName = childScan.BodyName + " Barycentre";
}
Scan barycentreScan = new()
{
Timestamp = barycentre.Timestamp,
BodyName = baryName,
Parents = childScan.Parents.RemoveAt(0),
PlanetClass = "Barycentre",
StarSystem = barycentre.StarSystem,
SystemAddress = barycentre.SystemAddress,
BodyID = barycentre.BodyID,
SemiMajorAxis = barycentre.SemiMajorAxis,
Eccentricity = barycentre.Eccentricity,
OrbitalInclination = barycentre.OrbitalInclination,
Periapsis = barycentre.Periapsis,
OrbitalPeriod = barycentre.OrbitalPeriod,
AscendingNode = barycentre.AscendingNode,
MeanAnomaly = barycentre.MeanAnomaly,
Json = barycentre.Json
};
return barycentreScan;
}
public void SetSystem(string potentialNewSystem)
{
if (string.IsNullOrEmpty(currentSystem) || currentSystem != potentialNewSystem)
{
currentSystem = potentialNewSystem;
if (ExplorerWorker.settings.OnlyShowCurrentSystem && !ObservatoryCore.IsLogMonitorBatchReading)
{
ObservatoryCore.ClearGrid(ExplorerWorker, new ExplorerUIResults());
Clear();
}
}
}
public void ProcessScan(Scan scanEvent, bool readAll)
{
if (!readAll)
{
string criteriaFilePath = ExplorerWorker.settings.CustomCriteriaFile;
if (File.Exists(criteriaFilePath))
{
DateTime fileModified = new FileInfo(criteriaFilePath).LastWriteTime;
if (fileModified != CriteriaLastModified)
{
try
{
CustomCriteriaManager.RefreshCriteria(criteriaFilePath);
}
catch (CriteriaLoadException e)
{
var exceptionResult = new ExplorerUIResults()
{
BodyName = "Error Reading Custom Criteria File",
Time = DateTime.Now.ToString("G"),
Description = e.Message,
Details = e.OriginalScript
};
ObservatoryCore.AddGridItem(ExplorerWorker, exceptionResult);
}
CriteriaLastModified = fileModified;
}
}
}
Dictionary<int, Scan> systemBodies;
if (SystemBodyHistory.ContainsKey(scanEvent.SystemAddress))
{
systemBodies = SystemBodyHistory[scanEvent.SystemAddress];
if (systemBodies.ContainsKey(scanEvent.BodyID))
{
if (scanEvent.SystemAddress != 0)
{
//We've already checked this object.
return;
}
}
}
else
{
systemBodies = new();
SystemBodyHistory.Add(scanEvent.SystemAddress, systemBodies);
}
if (SystemBodyHistory.Count > 1000)
{
foreach (var entry in SystemBodyHistory.Where(entry => entry.Key != scanEvent.SystemAddress).ToList())
{
SystemBodyHistory.Remove(entry.Key);
}
SystemBodyHistory.TrimExcess();
}
if (scanEvent.SystemAddress != 0 && !systemBodies.ContainsKey(scanEvent.BodyID))
systemBodies.Add(scanEvent.BodyID, scanEvent);
var results = DefaultCriteria.CheckInterest(scanEvent, SystemBodyHistory, BodySignalHistory, ExplorerWorker.settings);
if (BarycentreHistory.ContainsKey(scanEvent.SystemAddress) && scanEvent.Parent != null && BarycentreHistory[scanEvent.SystemAddress].ContainsKey(scanEvent.Parent[0].Body))
{
ProcessScan(ConvertBarycentre(BarycentreHistory[scanEvent.SystemAddress][scanEvent.Parent[0].Body], scanEvent), readAll);
}
if (ExplorerWorker.settings.EnableCustomCriteria)
results.AddRange(CustomCriteriaManager.CheckInterest(scanEvent, SystemBodyHistory, BodySignalHistory, ExplorerWorker.settings));
if (results.Count > 0)
{
StringBuilder notificationDetail = new();
StringBuilder notificationExtendedDetail = new();
foreach (var result in results)
{
var scanResult = new ExplorerUIResults()
{
BodyName = result.SystemWide ? scanEvent.StarSystem : scanEvent.BodyName,
Time = scanEvent.TimestampDateTime.ToString("G"),
Description = result.Description,
Details = result.Detail
};
ObservatoryCore.AddGridItem(ExplorerWorker, scanResult);
notificationDetail.AppendLine(result.Description);
notificationExtendedDetail.AppendLine(result.Detail);
}
string bodyAffix;
if (scanEvent.StarSystem != null && scanEvent.BodyName.StartsWith(scanEvent.StarSystem))
{
bodyAffix = scanEvent.BodyName.Replace(scanEvent.StarSystem, string.Empty);
}
else
{
bodyAffix = string.Empty;
}
string bodyLabel = System.Security.SecurityElement.Escape(scanEvent.PlanetClass == "Barycentre" ? "Barycentre" : "Body");
string spokenAffix;
if (bodyAffix.Length > 0)
{
if (bodyAffix.Contains("Ring"))
{
int ringIndex = bodyAffix.Length - 6;
spokenAffix =
"<say-as interpret-as=\"spell-out\">" + bodyAffix[..ringIndex]
+ "</say-as><break strength=\"weak\"/>" + SplitOrdinalForSsml(bodyAffix.Substring(ringIndex, 1))
+ bodyAffix[(ringIndex + 1)..];
}
else
{
spokenAffix = SplitOrdinalForSsml(bodyAffix);
}
}
else
{
bodyLabel = "Primary Star";
spokenAffix = string.Empty;
}
NotificationArgs args = new()
{
Title = bodyLabel + bodyAffix,
TitleSsml = $"<speak version=\"1.0\" xmlns=\"http://www.w3.org/2001/10/synthesis\" xml:lang=\"en-US\"><voice name=\"\">{bodyLabel} {spokenAffix}</voice></speak>",
Detail = notificationDetail.ToString(),
Sender = ExplorerWorker.ShortName,
ExtendedDetails = notificationExtendedDetail.ToString(),
CoalescingId = scanEvent.BodyID,
};
ObservatoryCore.SendNotification(args);
}
}
private static string SplitOrdinalForSsml(string ordinalString)
{
var ordinalSegments = ordinalString.Split(' ');
StringBuilder affix = new();
foreach (var ordinalSegment in ordinalSegments)
{
if (ordinalSegment.All(Char.IsDigit))
affix.Append(" " + ordinalSegment);
else
affix.Append("<say-as interpret-as=\"spell-out\">" + ordinalSegment + "</say-as>");
}
return affix.ToString();
}
}
}