feat: add IGalaxyRepository.GetAlarmAttributesAsync + AlarmAttributesSql
This commit is contained in:
@@ -114,6 +114,24 @@ public sealed class GalaxyRepository(GalaxyRepositoryOptions options) : IGalaxyR
|
||||
return rows;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<List<GalaxyAlarmAttributeRow>> GetAlarmAttributesAsync(CancellationToken ct = default)
|
||||
{
|
||||
List<GalaxyAlarmAttributeRow> rows = new();
|
||||
|
||||
using SqlConnection conn = new(options.ConnectionString);
|
||||
await conn.OpenAsync(ct).ConfigureAwait(false);
|
||||
|
||||
using SqlCommand cmd = new(AlarmAttributesSql, conn) { CommandTimeout = options.CommandTimeoutSeconds };
|
||||
using SqlDataReader reader = await cmd.ExecuteReaderAsync(ct).ConfigureAwait(false);
|
||||
|
||||
while (await reader.ReadAsync(ct).ConfigureAwait(false))
|
||||
{
|
||||
rows.Add(MapAlarmRow(reader.GetString(0), reader.GetString(1), reader.GetString(2)));
|
||||
}
|
||||
return rows;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Maps the SQL columns projected by <c>AlarmAttributesSql</c> onto a
|
||||
/// <see cref="GalaxyAlarmAttributeRow"/>.
|
||||
@@ -284,5 +302,55 @@ SELECT
|
||||
FROM ranked r
|
||||
LEFT JOIN data_type dt ON dt.mx_data_type = r.mx_data_type
|
||||
WHERE r.rn = 1
|
||||
ORDER BY r.tag_name, r.attribute_name";
|
||||
|
||||
// Returns one row per alarm-bearing attribute across all deployed objects. The three
|
||||
// projected columns (full_tag_reference, source_object_reference, area_name) are mapped
|
||||
// by MapAlarmRow. Only attributes whose owning object has an AlarmExtension primitive
|
||||
// instance matching the attribute name are returned, which is why the EXISTS correlated
|
||||
// sub-query against deployed_package_chain is needed rather than relying solely on
|
||||
// dynamic_attribute.mx_attribute_category.
|
||||
private const string AlarmAttributesSql = @"
|
||||
;WITH deployed_package_chain AS (
|
||||
SELECT g.gobject_id, p.package_id, p.derived_from_package_id, 0 AS depth
|
||||
FROM gobject g
|
||||
INNER JOIN package p ON p.package_id = g.deployed_package_id
|
||||
WHERE g.is_template = 0 AND g.deployed_package_id <> 0
|
||||
UNION ALL
|
||||
SELECT dpc.gobject_id, p.package_id, p.derived_from_package_id, dpc.depth + 1
|
||||
FROM deployed_package_chain dpc
|
||||
INNER JOIN package p ON p.package_id = dpc.derived_from_package_id
|
||||
WHERE dpc.derived_from_package_id <> 0 AND dpc.depth < 10
|
||||
),
|
||||
candidate AS (
|
||||
SELECT dpc.gobject_id, g.tag_name, da.attribute_name, dpc.depth
|
||||
FROM deployed_package_chain dpc
|
||||
INNER JOIN dynamic_attribute da ON da.package_id = dpc.package_id
|
||||
INNER JOIN gobject g ON g.gobject_id = dpc.gobject_id
|
||||
INNER JOIN template_definition td ON td.template_definition_id = g.template_definition_id
|
||||
WHERE td.category_id IN (1, 3, 4, 10, 11, 13, 17, 24, 26)
|
||||
AND da.attribute_name NOT LIKE '[_]%'
|
||||
AND da.attribute_name NOT LIKE '%.Description'
|
||||
AND da.mx_attribute_category IN (2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 24)
|
||||
),
|
||||
ranked AS (
|
||||
SELECT c.*, ROW_NUMBER() OVER (
|
||||
PARTITION BY c.gobject_id, c.attribute_name ORDER BY c.depth) AS rn
|
||||
FROM candidate c
|
||||
)
|
||||
SELECT
|
||||
r.tag_name + '.' + r.attribute_name AS full_tag_reference,
|
||||
r.tag_name AS source_object_reference,
|
||||
ISNULL(area.tag_name, '') AS area_name
|
||||
FROM ranked r
|
||||
INNER JOIN gobject g ON g.gobject_id = r.gobject_id
|
||||
LEFT JOIN gobject area ON area.gobject_id = g.area_gobject_id
|
||||
WHERE r.rn = 1
|
||||
AND EXISTS (
|
||||
SELECT 1 FROM deployed_package_chain dpc2
|
||||
INNER JOIN primitive_instance pi ON pi.package_id = dpc2.package_id AND pi.primitive_name = r.attribute_name
|
||||
INNER JOIN primitive_definition pd ON pd.primitive_definition_id = pi.primitive_definition_id AND pd.primitive_name = 'AlarmExtension'
|
||||
WHERE dpc2.gobject_id = r.gobject_id
|
||||
)
|
||||
ORDER BY r.tag_name, r.attribute_name";
|
||||
}
|
||||
|
||||
@@ -23,4 +23,9 @@ public interface IGalaxyRepository
|
||||
/// <summary>Retrieves all attributes for Galaxy objects from the repository.</summary>
|
||||
/// <param name="ct">Token to cancel the asynchronous operation.</param>
|
||||
Task<List<GalaxyAttributeRow>> GetAttributesAsync(CancellationToken ct = default);
|
||||
|
||||
/// <summary>Returns the alarm-bearing attributes across deployed Galaxy objects.</summary>
|
||||
/// <param name="ct">Cancellation token.</param>
|
||||
/// <returns>The alarm-bearing attribute rows.</returns>
|
||||
Task<List<GalaxyAlarmAttributeRow>> GetAlarmAttributesAsync(CancellationToken ct = default);
|
||||
}
|
||||
|
||||
@@ -12,14 +12,17 @@ internal sealed class FakeGalaxyRepository : IGalaxyRepository
|
||||
{
|
||||
private readonly IReadOnlyList<GalaxyHierarchyRow> _hierarchy;
|
||||
private readonly IReadOnlyList<GalaxyAttributeRow> _attributes;
|
||||
private readonly IReadOnlyList<GalaxyAlarmAttributeRow> _alarmAttributes;
|
||||
|
||||
public FakeGalaxyRepository(
|
||||
IReadOnlyList<GalaxyHierarchyRow> hierarchy,
|
||||
IReadOnlyList<GalaxyAttributeRow> attributes,
|
||||
DateTime? deployTime)
|
||||
DateTime? deployTime,
|
||||
IReadOnlyList<GalaxyAlarmAttributeRow>? alarmAttributes = null)
|
||||
{
|
||||
_hierarchy = hierarchy;
|
||||
_attributes = attributes;
|
||||
_alarmAttributes = alarmAttributes ?? Array.Empty<GalaxyAlarmAttributeRow>();
|
||||
DeployTime = deployTime;
|
||||
}
|
||||
|
||||
@@ -33,6 +36,8 @@ internal sealed class FakeGalaxyRepository : IGalaxyRepository
|
||||
|
||||
public int AttributeReadCount { get; private set; }
|
||||
|
||||
public int AlarmAttributeReadCount { get; private set; }
|
||||
|
||||
public Task<bool> TestConnectionAsync(CancellationToken ct = default) =>
|
||||
ThrowOnQuery is null ? Task.FromResult(true) : throw ThrowOnQuery;
|
||||
|
||||
@@ -67,6 +72,17 @@ internal sealed class FakeGalaxyRepository : IGalaxyRepository
|
||||
AttributeReadCount++;
|
||||
return Task.FromResult(_attributes.ToList());
|
||||
}
|
||||
|
||||
public Task<List<GalaxyAlarmAttributeRow>> GetAlarmAttributesAsync(CancellationToken ct = default)
|
||||
{
|
||||
if (ThrowOnQuery is not null)
|
||||
{
|
||||
throw ThrowOnQuery;
|
||||
}
|
||||
|
||||
AlarmAttributeReadCount++;
|
||||
return Task.FromResult(_alarmAttributes.ToList());
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Records published deploy events so tests can assert publication.</summary>
|
||||
|
||||
Reference in New Issue
Block a user