feat: add IGalaxyRepository.GetAlarmAttributesAsync + AlarmAttributesSql
This commit is contained in:
@@ -114,6 +114,24 @@ public sealed class GalaxyRepository(GalaxyRepositoryOptions options) : IGalaxyR
|
|||||||
return rows;
|
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>
|
/// <summary>
|
||||||
/// Maps the SQL columns projected by <c>AlarmAttributesSql</c> onto a
|
/// Maps the SQL columns projected by <c>AlarmAttributesSql</c> onto a
|
||||||
/// <see cref="GalaxyAlarmAttributeRow"/>.
|
/// <see cref="GalaxyAlarmAttributeRow"/>.
|
||||||
@@ -284,5 +302,55 @@ SELECT
|
|||||||
FROM ranked r
|
FROM ranked r
|
||||||
LEFT JOIN data_type dt ON dt.mx_data_type = r.mx_data_type
|
LEFT JOIN data_type dt ON dt.mx_data_type = r.mx_data_type
|
||||||
WHERE r.rn = 1
|
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";
|
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>
|
/// <summary>Retrieves all attributes for Galaxy objects from the repository.</summary>
|
||||||
/// <param name="ct">Token to cancel the asynchronous operation.</param>
|
/// <param name="ct">Token to cancel the asynchronous operation.</param>
|
||||||
Task<List<GalaxyAttributeRow>> GetAttributesAsync(CancellationToken ct = default);
|
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<GalaxyHierarchyRow> _hierarchy;
|
||||||
private readonly IReadOnlyList<GalaxyAttributeRow> _attributes;
|
private readonly IReadOnlyList<GalaxyAttributeRow> _attributes;
|
||||||
|
private readonly IReadOnlyList<GalaxyAlarmAttributeRow> _alarmAttributes;
|
||||||
|
|
||||||
public FakeGalaxyRepository(
|
public FakeGalaxyRepository(
|
||||||
IReadOnlyList<GalaxyHierarchyRow> hierarchy,
|
IReadOnlyList<GalaxyHierarchyRow> hierarchy,
|
||||||
IReadOnlyList<GalaxyAttributeRow> attributes,
|
IReadOnlyList<GalaxyAttributeRow> attributes,
|
||||||
DateTime? deployTime)
|
DateTime? deployTime,
|
||||||
|
IReadOnlyList<GalaxyAlarmAttributeRow>? alarmAttributes = null)
|
||||||
{
|
{
|
||||||
_hierarchy = hierarchy;
|
_hierarchy = hierarchy;
|
||||||
_attributes = attributes;
|
_attributes = attributes;
|
||||||
|
_alarmAttributes = alarmAttributes ?? Array.Empty<GalaxyAlarmAttributeRow>();
|
||||||
DeployTime = deployTime;
|
DeployTime = deployTime;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -33,6 +36,8 @@ internal sealed class FakeGalaxyRepository : IGalaxyRepository
|
|||||||
|
|
||||||
public int AttributeReadCount { get; private set; }
|
public int AttributeReadCount { get; private set; }
|
||||||
|
|
||||||
|
public int AlarmAttributeReadCount { get; private set; }
|
||||||
|
|
||||||
public Task<bool> TestConnectionAsync(CancellationToken ct = default) =>
|
public Task<bool> TestConnectionAsync(CancellationToken ct = default) =>
|
||||||
ThrowOnQuery is null ? Task.FromResult(true) : throw ThrowOnQuery;
|
ThrowOnQuery is null ? Task.FromResult(true) : throw ThrowOnQuery;
|
||||||
|
|
||||||
@@ -67,6 +72,17 @@ internal sealed class FakeGalaxyRepository : IGalaxyRepository
|
|||||||
AttributeReadCount++;
|
AttributeReadCount++;
|
||||||
return Task.FromResult(_attributes.ToList());
|
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>
|
/// <summary>Records published deploy events so tests can assert publication.</summary>
|
||||||
|
|||||||
Reference in New Issue
Block a user