From 89d979e6a5ca32bcdd85daf789910305e44a8dd1 Mon Sep 17 00:00:00 2001 From: Dave Thaler Date: Tue, 8 Oct 2024 18:28:05 -0700 Subject: [PATCH] Show dev nodes on dashboard (#147) * Show dev nodes on dashboard Fixes #145 Signed-off-by: Dave Thaler * Store orcasound site (dev vs prod) in cosmos db Signed-off-by: Dave Thaler --------- Signed-off-by: Dave Thaler --- OrcanodeMonitor/Core/Fetcher.cs | 263 +++++++++++++++++------------ OrcanodeMonitor/Models/Orcanode.cs | 24 +++ OrcanodeMonitor/Pages/Index.cshtml | 16 +- 3 files changed, 187 insertions(+), 116 deletions(-) diff --git a/OrcanodeMonitor/Core/Fetcher.cs b/OrcanodeMonitor/Core/Fetcher.cs index 348b487..8f4261e 100644 --- a/OrcanodeMonitor/Core/Fetcher.cs +++ b/OrcanodeMonitor/Core/Fetcher.cs @@ -28,7 +28,9 @@ public class Fetcher { private static TimeZoneInfo _pacificTimeZone = TimeZoneInfo.FindSystemTimeZoneById("America/Los_Angeles"); private static HttpClient _httpClient = new HttpClient(); - private static string _orcasoundFeedsUrl = "https://live.orcasound.net/api/json/feeds"; + private static string _orcasoundProdSite = "live.orcasound.net"; + private static string _orcasoundDevSite = "dev.orcasound.net"; + private static string _orcasoundFeedsUrlPath = "/api/json/feeds"; private static string _dataplicityDevicesUrl = "https://apps.dataplicity.com/devices/"; private static string _orcaHelloHydrophonesUrl = "https://aifororcasdetections2.azurewebsites.net/api/hydrophones"; private static DateTime _unixEpoch = new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc); @@ -447,147 +449,188 @@ public async static Task UpdateDataplicityDataAsync(OrcanodeMonitorContext conte } } - /// - /// Update the current list of Orcanodes using data from orcasound.net. - /// - /// Database context to update - /// - public async static Task UpdateOrcasoundDataAsync(OrcanodeMonitorContext context) + private async static Task GetOrcasoundDataAsync(OrcanodeMonitorContext context, string site) { try { - string json = await _httpClient.GetStringAsync(_orcasoundFeedsUrl); + string url = "https://" + site + _orcasoundFeedsUrlPath; + string json = await _httpClient.GetStringAsync(url); if (json.IsNullOrEmpty()) { - return; + return null; } dynamic response = JsonSerializer.Deserialize(json); if (response == null) { - return; + return null; } JsonElement dataArray = response.data; if (dataArray.ValueKind != JsonValueKind.Array) { - return; + return null; } + return dataArray; + } + catch (Exception ex) + { + string msg = ex.ToString(); + return null; + } + } - var foundList = context.Orcanodes.ToList(); + private static void UpdateOrcasoundNode(JsonElement feed, List foundList, List unfoundList, OrcanodeMonitorContext context, string site) + { + if (!feed.TryGetProperty("id", out var feedId)) + { + return; + } + if (!feed.TryGetProperty("attributes", out JsonElement attributes)) + { + return; + } + if (!attributes.TryGetProperty("name", out var name)) + { + return; + } + string orcasoundName = name.ToString(); + if (!attributes.TryGetProperty("dataplicity_id", out var dataplicity_id)) + { + return; + } + bool hidden = false; + if (attributes.TryGetProperty("hidden", out var hiddenElement)) + { + hidden = hiddenElement.GetBoolean(); + } + string dataplicitySerial = dataplicity_id.ToString(); - // Create a list to track what nodes are no longer returned. - var unfoundList = foundList.ToList(); + // Remove the found node from the unfound list. + Orcanode? oldListNode = unfoundList.Find(a => a.OrcasoundFeedId == feedId.ToString()); + if (oldListNode != null) + { + unfoundList.Remove(oldListNode); + } - foreach (JsonElement feed in dataArray.EnumerateArray()) + Orcanode? node = null; + node = FindOrcanodeByOrcasoundFeedId(foundList, feedId.ToString()); + if (node == null) + { + // We didn't used to store the feed ID, only the name, so try again by name. + Orcanode? possibleNode = FindOrcanodeByOrcasoundName(foundList, orcasoundName); + if ((possibleNode != null) && possibleNode.OrcasoundFeedId.IsNullOrEmpty()) { - if (!feed.TryGetProperty("id", out var feedId)) - { - continue; - } - if (!feed.TryGetProperty("attributes", out JsonElement attributes)) - { - continue; - } - if (!attributes.TryGetProperty("name", out var name)) - { - continue; - } - string orcasoundName = name.ToString(); - if (!attributes.TryGetProperty("dataplicity_id", out var dataplicity_id)) - { - continue; - } - bool hidden = false; - if (attributes.TryGetProperty("hidden", out var hiddenElement)) - { - hidden = hiddenElement.GetBoolean(); - } - string dataplicitySerial = dataplicity_id.ToString(); - - // Remove the found node from the unfound list. - Orcanode? oldListNode = unfoundList.Find(a => a.OrcasoundFeedId == feedId.ToString()); + node = possibleNode; + oldListNode = unfoundList.Find(a => a.OrcasoundName == orcasoundName); if (oldListNode != null) { unfoundList.Remove(oldListNode); } + } + } - Orcanode? node = null; - node = FindOrcanodeByOrcasoundFeedId(foundList, feedId.ToString()); + // See if we can find a node by dataplicity ID, so that if a node + // shows up in dataplicity first and Orcasite later, we don't create a + // duplicate entry. + Orcanode? dataplicityNode = null; + if (!dataplicitySerial.IsNullOrEmpty()) + { + dataplicityNode = FindOrcanodeByDataplicitySerial(foundList, dataplicitySerial, out OrcanodeOnlineStatus oldStatus); + if (dataplicityNode != null) + { if (node == null) { - // We didn't used to store the feed ID, only the name, so try again by name. - node = FindOrcanodeByOrcasoundName(foundList, orcasoundName); + node = dataplicityNode; } - - // See if we can find a node by dataplicity ID, so that if a node - // shows up in dataplicity first and Orcasite later, we don't create a - // duplicate entry. - Orcanode? dataplicityNode = null; - if (!dataplicitySerial.IsNullOrEmpty()) + else if (node != dataplicityNode) { - dataplicityNode = FindOrcanodeByDataplicitySerial(foundList, dataplicitySerial, out OrcanodeOnlineStatus oldStatus); - if (dataplicityNode != null) - { - if (node == null) - { - node = dataplicityNode; - } - else if (node != dataplicityNode) - { - // We have duplicate nodes to merge. In theory we shouldn't have any - // node state for the dataplicity-only node. (TODO: verify this) - node.DataplicityDescription = dataplicityNode.DataplicityDescription; - node.DataplicityName = dataplicityNode.DataplicityName; - node.DataplicityOnline = dataplicityNode.DataplicityOnline; - node.AgentVersion = dataplicityNode.AgentVersion; - node.DiskCapacity = dataplicityNode.DiskCapacity; - node.DiskUsed = dataplicityNode.DiskUsed; - node.DataplicityUpgradeAvailable = dataplicityNode.DataplicityUpgradeAvailable; - context.Orcanodes.Remove(dataplicityNode); - } - } + // We have duplicate nodes to merge. In theory we shouldn't have any + // node state for the dataplicity-only node. (TODO: verify this) + node.DataplicityDescription = dataplicityNode.DataplicityDescription; + node.DataplicityName = dataplicityNode.DataplicityName; + node.DataplicityOnline = dataplicityNode.DataplicityOnline; + node.AgentVersion = dataplicityNode.AgentVersion; + node.DiskCapacity = dataplicityNode.DiskCapacity; + node.DiskUsed = dataplicityNode.DiskUsed; + node.DataplicityUpgradeAvailable = dataplicityNode.DataplicityUpgradeAvailable; + context.Orcanodes.Remove(dataplicityNode); } + } + } - if (node == null) - { - node = CreateOrcanode(context.Orcanodes); - node.OrcasoundName = name.ToString(); - } + if (node == null) + { + node = CreateOrcanode(context.Orcanodes); + node.OrcasoundName = name.ToString(); + } - if (!dataplicitySerial.IsNullOrEmpty()) - { - if (!node.DataplicitySerial.IsNullOrEmpty() && dataplicitySerial != node.DataplicitySerial) - { - // TODO: The orcasound entry for the node changed its dataplicity_id. - } - node.DataplicitySerial = dataplicitySerial; - } + if (!dataplicitySerial.IsNullOrEmpty()) + { + if (!node.DataplicitySerial.IsNullOrEmpty() && dataplicitySerial != node.DataplicitySerial) + { + // TODO: The orcasound entry for the node changed its dataplicity_id. + } + node.DataplicitySerial = dataplicitySerial; + } - if (node.OrcasoundFeedId.IsNullOrEmpty()) - { - node.OrcasoundFeedId = feedId.ToString(); - } - if (orcasoundName != node.OrcasoundName) - { - // We just detected a name change. - node.OrcasoundName = orcasoundName; - } - if (attributes.TryGetProperty("node_name", out var nodeName)) - { - node.S3NodeName = nodeName.ToString(); - } - if (attributes.TryGetProperty("bucket", out var bucket)) - { - node.S3Bucket = bucket.ToString(); - } - if (attributes.TryGetProperty("slug", out var slug)) - { - node.OrcasoundSlug = slug.ToString(); - } - if (attributes.TryGetProperty("visible", out var visible)) - { - node.OrcasoundVisible = visible.GetBoolean(); - } + if (node.OrcasoundFeedId.IsNullOrEmpty()) + { + node.OrcasoundFeedId = feedId.ToString(); + } + if (!site.IsNullOrEmpty()) + { + node.OrcasoundHost = site; + } + if (orcasoundName != node.OrcasoundName) + { + // We just detected a name change. + node.OrcasoundName = orcasoundName; + } + if (attributes.TryGetProperty("node_name", out var nodeName)) + { + node.S3NodeName = nodeName.ToString(); + } + if (attributes.TryGetProperty("bucket", out var bucket)) + { + node.S3Bucket = bucket.ToString(); + } + if (attributes.TryGetProperty("slug", out var slug)) + { + node.OrcasoundSlug = slug.ToString(); + } + if (attributes.TryGetProperty("visible", out var visible)) + { + node.OrcasoundVisible = visible.GetBoolean(); + } + } + + private async static Task UpdateOrcasoundSiteDataAsync(OrcanodeMonitorContext context, string site, List foundList, List unfoundList) + { + JsonElement? dataArray = await GetOrcasoundDataAsync(context, site); + if (dataArray.HasValue) + { + foreach (JsonElement feed in dataArray.Value.EnumerateArray()) + { + UpdateOrcasoundNode(feed, foundList, unfoundList, context, site); } + } + } + + /// + /// Update the current list of Orcanodes using data from orcasound.net. + /// + /// Database context to update + /// + public async static Task UpdateOrcasoundDataAsync(OrcanodeMonitorContext context) + { + var foundList = context.Orcanodes.ToList(); + + // Create a list to track what nodes are no longer returned. + var unfoundList = foundList.ToList(); + + try + { + await UpdateOrcasoundSiteDataAsync(context, _orcasoundProdSite, foundList, unfoundList); + await UpdateOrcasoundSiteDataAsync(context, _orcasoundDevSite, foundList, unfoundList); // Mark any remaining unfound nodes as absent. foreach (var unfoundNode in unfoundList) diff --git a/OrcanodeMonitor/Models/Orcanode.cs b/OrcanodeMonitor/Models/Orcanode.cs index eca79aa..2bd2c48 100644 --- a/OrcanodeMonitor/Models/Orcanode.cs +++ b/OrcanodeMonitor/Models/Orcanode.cs @@ -51,6 +51,7 @@ public Orcanode() OrcasoundName = string.Empty; OrcasoundSlug = string.Empty; OrcasoundFeedId = string.Empty; + OrcasoundHost = string.Empty; S3Bucket = string.Empty; S3NodeName = string.Empty; AgentVersion = string.Empty; @@ -187,6 +188,11 @@ public Orcanode() /// Audio stream status of most recent sample (defaults to absent). /// public OrcanodeOnlineStatus? AudioStreamStatus { get; set; } + + /// + /// Orcasound site host (defaults to empty). + /// + public string OrcasoundHost { get; set; } #endregion persisted @@ -300,6 +306,24 @@ public OrcanodeOnlineStatus S3StreamStatus } } + private bool IsDev + { + get + { + if (this.S3Bucket.StartsWith("dev")) + { + return true; + } + if (this.DataplicityName.ToLower().StartsWith("dev")) + { + return true; + } + return false; + } + } + + public string Type => IsDev ? "Dev" : "Prod"; + public string OrcasoundOnlineStatusString { get { diff --git a/OrcanodeMonitor/Pages/Index.cshtml b/OrcanodeMonitor/Pages/Index.cshtml index 06ccfc3..018e75d 100644 --- a/OrcanodeMonitor/Pages/Index.cshtml +++ b/OrcanodeMonitor/Pages/Index.cshtml @@ -12,6 +12,7 @@ +
LocationType