e00ee61cf0
Group the two double-width timestamp cards at the start of the metric row so the deploy/refresh pair reads together, ahead of the count cards. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
198 lines
7.5 KiB
Plaintext
198 lines
7.5 KiB
Plaintext
@page "/galaxy"
|
|
@page "/dashboard/galaxy"
|
|
@inherits DashboardPageBase
|
|
|
|
<PageTitle>Dashboard Galaxy</PageTitle>
|
|
|
|
@if (Snapshot is null)
|
|
{
|
|
<div class="empty-state">Loading Galaxy summary.</div>
|
|
}
|
|
else
|
|
{
|
|
<div class="dashboard-page-header">
|
|
<div>
|
|
<h1>Galaxy Repository</h1>
|
|
<div class="text-secondary">@RefreshHeading()</div>
|
|
</div>
|
|
<StatusBadge Text="@Snapshot.Galaxy.Status.ToString()" />
|
|
</div>
|
|
|
|
<section class="metric-grid">
|
|
<MetricCard Label="Last Deploy" Value="@DashboardDisplay.DateTime(Snapshot.Galaxy.LastDeployTime)" Detail="@DeployAge()" Wide="true" />
|
|
<MetricCard Label="Last Refresh" Value="@DashboardDisplay.DateTime(Snapshot.Galaxy.LastSuccessAt)" Detail="@LastAttemptDetail()" Wide="true" />
|
|
<MetricCard Label="Objects" Value="@DashboardDisplay.Count(Snapshot.Galaxy.ObjectCount)" Detail="@($"{Snapshot.Galaxy.AreaCount:N0} areas")" />
|
|
<MetricCard Label="Attributes" Value="@DashboardDisplay.Count(Snapshot.Galaxy.AttributeCount)" Detail="dynamic, deployed" />
|
|
<MetricCard Label="Historized" Value="@DashboardDisplay.Count(Snapshot.Galaxy.HistorizedAttributeCount)" />
|
|
<MetricCard Label="Alarms" Value="@DashboardDisplay.Count(Snapshot.Galaxy.AlarmAttributeCount)" />
|
|
</section>
|
|
|
|
@if (Snapshot.Galaxy.Status == DashboardGalaxyStatus.Unknown)
|
|
{
|
|
<section class="dashboard-section">
|
|
<div class="empty-state">
|
|
Galaxy summary has not been collected yet. The dashboard refreshes the
|
|
summary every @RefreshIntervalSeconds() seconds via the
|
|
<code>GalaxyRepository</code> service.
|
|
</div>
|
|
</section>
|
|
}
|
|
|
|
@if (!string.IsNullOrWhiteSpace(Snapshot.Galaxy.LastError))
|
|
{
|
|
<section class="dashboard-section">
|
|
<div class="section-heading">
|
|
<h2>Last Error</h2>
|
|
</div>
|
|
<div class="empty-state">@Snapshot.Galaxy.LastError</div>
|
|
</section>
|
|
}
|
|
|
|
<section class="dashboard-section">
|
|
<div class="section-heading">
|
|
<h2>Object Categories</h2>
|
|
</div>
|
|
@if (Snapshot.Galaxy.ObjectCategories.Count == 0)
|
|
{
|
|
<div class="empty-state">No deployed objects observed.</div>
|
|
}
|
|
else
|
|
{
|
|
<div class="table-responsive">
|
|
<table class="table table-sm dashboard-table">
|
|
<thead>
|
|
<tr>
|
|
<th scope="col">Category</th>
|
|
<th scope="col">Category ID</th>
|
|
<th scope="col">Objects</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
@foreach (DashboardGalaxyCategoryCount row in Snapshot.Galaxy.ObjectCategories)
|
|
{
|
|
<tr>
|
|
<td>@row.CategoryName</td>
|
|
<td><code>@row.CategoryId</code></td>
|
|
<td>@DashboardDisplay.Count(row.ObjectCount)</td>
|
|
</tr>
|
|
}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
}
|
|
</section>
|
|
|
|
<section class="dashboard-section">
|
|
<div class="section-heading">
|
|
<h2>Top Templates</h2>
|
|
</div>
|
|
@if (Snapshot.Galaxy.TopTemplates.Count == 0)
|
|
{
|
|
<div class="empty-state">No template usage observed.</div>
|
|
}
|
|
else
|
|
{
|
|
<div class="table-responsive">
|
|
<table class="table table-sm dashboard-table">
|
|
<thead>
|
|
<tr>
|
|
<th scope="col">Template</th>
|
|
<th scope="col">Instances</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
@foreach (DashboardGalaxyTemplateUsage row in Snapshot.Galaxy.TopTemplates)
|
|
{
|
|
<tr>
|
|
<td><code>@row.TemplateName</code></td>
|
|
<td>@DashboardDisplay.Count(row.InstanceCount)</td>
|
|
</tr>
|
|
}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
}
|
|
</section>
|
|
|
|
<section class="dashboard-section">
|
|
<div class="section-heading">
|
|
<h2>Sync Info</h2>
|
|
</div>
|
|
<div class="table-responsive">
|
|
<table class="table table-sm dashboard-table details-table">
|
|
<tbody>
|
|
<tr><th scope="row">Status</th><td><StatusBadge Text="@Snapshot.Galaxy.Status.ToString()" /></td></tr>
|
|
<tr><th scope="row">Last successful refresh</th><td>@DashboardDisplay.DateTime(Snapshot.Galaxy.LastSuccessAt)</td></tr>
|
|
<tr><th scope="row">Last attempt</th><td>@DashboardDisplay.DateTime(Snapshot.Galaxy.LastQueriedAt)</td></tr>
|
|
<tr><th scope="row">Galaxy <code>time_of_last_deploy</code></th><td>@DashboardDisplay.DateTime(Snapshot.Galaxy.LastDeployTime)</td></tr>
|
|
<tr><th scope="row">Refresh interval</th><td>@RefreshIntervalSeconds() seconds</td></tr>
|
|
<tr><th scope="row">Connection string</th><td><code>@DashboardDisplay.Text(GalaxyConnectionStringDisplay())</code></td></tr>
|
|
<tr><th scope="row">Command timeout</th><td>@CommandTimeoutSeconds() seconds</td></tr>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
<div class="text-secondary small mt-2">
|
|
Browse data is served by the <code>galaxy_repository.v1.GalaxyRepository</code> gRPC
|
|
service. Clients call <code>DiscoverHierarchy</code> for the full tree and
|
|
<code>GetLastDeployTime</code> to detect redeployments.
|
|
</div>
|
|
</section>
|
|
}
|
|
|
|
@code {
|
|
[Inject]
|
|
private IOptions<MxGateway.Server.Galaxy.GalaxyRepositoryOptions> GalaxyOptions { get; set; } = null!;
|
|
|
|
private string RefreshHeading()
|
|
{
|
|
DashboardGalaxySummary galaxy = Snapshot!.Galaxy;
|
|
return galaxy.LastSuccessAt is null
|
|
? "Awaiting first successful refresh"
|
|
: $"Refreshed {DashboardDisplay.DateTime(galaxy.LastSuccessAt)}";
|
|
}
|
|
|
|
private string? DeployAge()
|
|
{
|
|
DashboardGalaxySummary galaxy = Snapshot!.Galaxy;
|
|
if (galaxy.LastDeployTime is null || galaxy.LastSuccessAt is null)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
TimeSpan age = galaxy.LastSuccessAt.Value - galaxy.LastDeployTime.Value;
|
|
if (age < TimeSpan.Zero)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
return $"{DashboardDisplay.Duration(age)} ago";
|
|
}
|
|
|
|
private string? LastAttemptDetail()
|
|
{
|
|
DashboardGalaxySummary galaxy = Snapshot!.Galaxy;
|
|
if (galaxy.LastQueriedAt is null)
|
|
{
|
|
return "never queried";
|
|
}
|
|
|
|
if (galaxy.LastSuccessAt is null)
|
|
{
|
|
return "no successful refresh yet";
|
|
}
|
|
|
|
return galaxy.LastQueriedAt > galaxy.LastSuccessAt
|
|
? $"last attempt {DashboardDisplay.DateTime(galaxy.LastQueriedAt)}"
|
|
: null;
|
|
}
|
|
|
|
private int RefreshIntervalSeconds() => Math.Max(1, GalaxyOptions.Value.DashboardRefreshIntervalSeconds);
|
|
|
|
private int CommandTimeoutSeconds() => GalaxyOptions.Value.CommandTimeoutSeconds;
|
|
|
|
private string GalaxyConnectionStringDisplay()
|
|
{
|
|
return DashboardConnectionStringDisplay.GalaxyRepositoryConnectionString(GalaxyOptions.Value.ConnectionString);
|
|
}
|
|
}
|