feat(templateengine): validate native alarm source connection + source reference
This commit is contained in:
@@ -80,5 +80,6 @@ public enum ValidationCategory
|
|||||||
OnTriggerScriptNotFound,
|
OnTriggerScriptNotFound,
|
||||||
CrossCallViolation,
|
CrossCallViolation,
|
||||||
MissingMetadata,
|
MissingMetadata,
|
||||||
ConnectionConfig
|
ConnectionConfig,
|
||||||
|
NativeAlarmSourceInvalid
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,7 +27,8 @@ public class SemanticValidator
|
|||||||
/// <param name="sharedScripts">Shared scripts available for CallShared references.</param>
|
/// <param name="sharedScripts">Shared scripts available for CallShared references.</param>
|
||||||
public ValidationResult Validate(
|
public ValidationResult Validate(
|
||||||
FlattenedConfiguration configuration,
|
FlattenedConfiguration configuration,
|
||||||
IReadOnlyList<ResolvedScript>? sharedScripts = null)
|
IReadOnlyList<ResolvedScript>? sharedScripts = null,
|
||||||
|
IReadOnlySet<string>? alarmCapableConnectionNames = null)
|
||||||
{
|
{
|
||||||
var errors = new List<ValidationEntry>();
|
var errors = new List<ValidationEntry>();
|
||||||
var warnings = new List<ValidationEntry>();
|
var warnings = new List<ValidationEntry>();
|
||||||
@@ -215,6 +216,33 @@ public class SemanticValidator
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Native alarm source bindings: connection + source reference must be
|
||||||
|
// present, and (when the alarm-capable connection set is supplied) the
|
||||||
|
// connection must resolve to an alarm-capable site data connection.
|
||||||
|
foreach (var nativeSource in configuration.NativeAlarmSources)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(nativeSource.SourceReference))
|
||||||
|
{
|
||||||
|
errors.Add(ValidationEntry.Error(ValidationCategory.NativeAlarmSourceInvalid,
|
||||||
|
$"Native alarm source '{nativeSource.CanonicalName}' has an empty source reference.",
|
||||||
|
nativeSource.CanonicalName));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (string.IsNullOrWhiteSpace(nativeSource.ConnectionName))
|
||||||
|
{
|
||||||
|
errors.Add(ValidationEntry.Error(ValidationCategory.NativeAlarmSourceInvalid,
|
||||||
|
$"Native alarm source '{nativeSource.CanonicalName}' has no data connection.",
|
||||||
|
nativeSource.CanonicalName));
|
||||||
|
}
|
||||||
|
else if (alarmCapableConnectionNames is not null &&
|
||||||
|
!alarmCapableConnectionNames.Contains(nativeSource.ConnectionName))
|
||||||
|
{
|
||||||
|
errors.Add(ValidationEntry.Error(ValidationCategory.NativeAlarmSourceInvalid,
|
||||||
|
$"Native alarm source '{nativeSource.CanonicalName}' references connection '{nativeSource.ConnectionName}' which is not an alarm-capable data connection on this site.",
|
||||||
|
nativeSource.CanonicalName));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return new ValidationResult { Errors = errors, Warnings = warnings };
|
return new ValidationResult { Errors = errors, Warnings = warnings };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -7,6 +7,42 @@ public class SemanticValidatorTests
|
|||||||
{
|
{
|
||||||
private readonly SemanticValidator _sut = new();
|
private readonly SemanticValidator _sut = new();
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Validate_NativeAlarmSource_UnknownConnection_ReturnsError()
|
||||||
|
{
|
||||||
|
var cfg = new FlattenedConfiguration
|
||||||
|
{
|
||||||
|
InstanceUniqueName = "Instance1",
|
||||||
|
NativeAlarmSources = [new ResolvedNativeAlarmSource { CanonicalName = "P", ConnectionName = "Ghost", SourceReference = "x" }]
|
||||||
|
};
|
||||||
|
var result = _sut.Validate(cfg, alarmCapableConnectionNames: new HashSet<string> { "RealConn" });
|
||||||
|
Assert.Contains(result.Errors, e => e.Category == ValidationCategory.NativeAlarmSourceInvalid);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Validate_NativeAlarmSource_EmptySourceRef_ReturnsError()
|
||||||
|
{
|
||||||
|
var cfg = new FlattenedConfiguration
|
||||||
|
{
|
||||||
|
InstanceUniqueName = "Instance1",
|
||||||
|
NativeAlarmSources = [new ResolvedNativeAlarmSource { CanonicalName = "P", ConnectionName = "RealConn", SourceReference = "" }]
|
||||||
|
};
|
||||||
|
var result = _sut.Validate(cfg, alarmCapableConnectionNames: new HashSet<string> { "RealConn" });
|
||||||
|
Assert.Contains(result.Errors, e => e.Category == ValidationCategory.NativeAlarmSourceInvalid);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Validate_NativeAlarmSource_ValidBinding_NoError()
|
||||||
|
{
|
||||||
|
var cfg = new FlattenedConfiguration
|
||||||
|
{
|
||||||
|
InstanceUniqueName = "Instance1",
|
||||||
|
NativeAlarmSources = [new ResolvedNativeAlarmSource { CanonicalName = "P", ConnectionName = "RealConn", SourceReference = "ns=2;s=T1" }]
|
||||||
|
};
|
||||||
|
var result = _sut.Validate(cfg, alarmCapableConnectionNames: new HashSet<string> { "RealConn" });
|
||||||
|
Assert.DoesNotContain(result.Errors, e => e.Category == ValidationCategory.NativeAlarmSourceInvalid);
|
||||||
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void Validate_CallScriptTargetNotFound_ReturnsError()
|
public void Validate_CallScriptTargetNotFound_ReturnsError()
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in New Issue
Block a user