diff --git a/src/ScadaLink.CLI/Commands/DataConnectionCommands.cs b/src/ScadaLink.CLI/Commands/DataConnectionCommands.cs index 723bf00..6ef8250 100644 --- a/src/ScadaLink.CLI/Commands/DataConnectionCommands.cs +++ b/src/ScadaLink.CLI/Commands/DataConnectionCommands.cs @@ -38,7 +38,7 @@ public static class DataConnectionCommands var idOption = new Option("--id") { Description = "Data connection ID", Required = true }; var nameOption = new Option("--name") { Description = "Connection name", Required = true }; var protocolOption = new Option("--protocol") { Description = "Protocol", Required = true }; - var configOption = new Option("--configuration") { Description = "Configuration JSON" }; + var configOption = new Option("--configuration") { Description = "Primary configuration JSON" }; var cmd = new Command("update") { Description = "Update a data connection" }; cmd.Add(idOption); @@ -77,7 +77,7 @@ public static class DataConnectionCommands var siteIdOption = new Option("--site-id") { Description = "Site ID", Required = true }; var nameOption = new Option("--name") { Description = "Connection name", Required = true }; var protocolOption = new Option("--protocol") { Description = "Protocol (e.g. OpcUa)", Required = true }; - var configOption = new Option("--configuration") { Description = "Connection configuration JSON" }; + var configOption = new Option("--configuration") { Description = "Primary configuration JSON" }; var cmd = new Command("create") { Description = "Create a new data connection" }; cmd.Add(siteIdOption); diff --git a/src/ScadaLink.CentralUI/Components/Pages/Admin/DataConnectionForm.razor b/src/ScadaLink.CentralUI/Components/Pages/Admin/DataConnectionForm.razor index e5b1fb3..cfba6a5 100644 --- a/src/ScadaLink.CentralUI/Components/Pages/Admin/DataConnectionForm.razor +++ b/src/ScadaLink.CentralUI/Components/Pages/Admin/DataConnectionForm.razor @@ -100,7 +100,7 @@ _siteName = _sites.FirstOrDefault(s => s.Id == _formSiteId)?.Name ?? $"Site {_formSiteId}"; _formName = _editingConnection.Name; _formProtocol = _editingConnection.Protocol; - _formConfiguration = _editingConnection.Configuration; + _formConfiguration = _editingConnection.PrimaryConfiguration; } } catch (Exception ex) @@ -124,14 +124,14 @@ { _editingConnection.Name = _formName.Trim(); _editingConnection.Protocol = _formProtocol; - _editingConnection.Configuration = _formConfiguration?.Trim(); + _editingConnection.PrimaryConfiguration = _formConfiguration?.Trim(); await SiteRepository.UpdateDataConnectionAsync(_editingConnection); } else { var conn = new DataConnection(_formName.Trim(), _formProtocol, _formSiteId) { - Configuration = _formConfiguration?.Trim() + PrimaryConfiguration = _formConfiguration?.Trim() }; await SiteRepository.AddDataConnectionAsync(conn); } diff --git a/src/ScadaLink.CentralUI/Components/Pages/Admin/DataConnections.razor b/src/ScadaLink.CentralUI/Components/Pages/Admin/DataConnections.razor index 90353f3..bca9d3b 100644 --- a/src/ScadaLink.CentralUI/Components/Pages/Admin/DataConnections.razor +++ b/src/ScadaLink.CentralUI/Components/Pages/Admin/DataConnections.razor @@ -50,7 +50,7 @@ @conn.Name @conn.Protocol @(_siteLookup.GetValueOrDefault(conn.SiteId)?.Name ?? $"Site {conn.SiteId}") - @(conn.Configuration ?? "—") + @(conn.PrimaryConfiguration ?? "—") diff --git a/src/ScadaLink.Commons/Entities/Sites/DataConnection.cs b/src/ScadaLink.Commons/Entities/Sites/DataConnection.cs index 16df869..7721435 100644 --- a/src/ScadaLink.Commons/Entities/Sites/DataConnection.cs +++ b/src/ScadaLink.Commons/Entities/Sites/DataConnection.cs @@ -6,7 +6,9 @@ public class DataConnection public int SiteId { get; set; } public string Name { get; set; } public string Protocol { get; set; } - public string? Configuration { get; set; } + public string? PrimaryConfiguration { get; set; } + public string? BackupConfiguration { get; set; } + public int FailoverRetryCount { get; set; } = 3; public DataConnection(string name, string protocol, int siteId) { diff --git a/src/ScadaLink.Commons/Messages/Artifacts/DataConnectionArtifact.cs b/src/ScadaLink.Commons/Messages/Artifacts/DataConnectionArtifact.cs index 514adc4..00187b4 100644 --- a/src/ScadaLink.Commons/Messages/Artifacts/DataConnectionArtifact.cs +++ b/src/ScadaLink.Commons/Messages/Artifacts/DataConnectionArtifact.cs @@ -3,4 +3,6 @@ namespace ScadaLink.Commons.Messages.Artifacts; public record DataConnectionArtifact( string Name, string Protocol, - string? ConfigurationJson); + string? PrimaryConfigurationJson, + string? BackupConfigurationJson, + int FailoverRetryCount = 3); diff --git a/src/ScadaLink.Commons/Messages/Management/DataConnectionCommands.cs b/src/ScadaLink.Commons/Messages/Management/DataConnectionCommands.cs index 1e33aab..7ea35ef 100644 --- a/src/ScadaLink.Commons/Messages/Management/DataConnectionCommands.cs +++ b/src/ScadaLink.Commons/Messages/Management/DataConnectionCommands.cs @@ -2,6 +2,6 @@ namespace ScadaLink.Commons.Messages.Management; public record ListDataConnectionsCommand(int? SiteId = null); public record GetDataConnectionCommand(int DataConnectionId); -public record CreateDataConnectionCommand(int SiteId, string Name, string Protocol, string? Configuration); -public record UpdateDataConnectionCommand(int DataConnectionId, string Name, string Protocol, string? Configuration); +public record CreateDataConnectionCommand(int SiteId, string Name, string Protocol, string? PrimaryConfiguration); +public record UpdateDataConnectionCommand(int DataConnectionId, string Name, string Protocol, string? PrimaryConfiguration); public record DeleteDataConnectionCommand(int DataConnectionId); diff --git a/src/ScadaLink.ConfigurationDatabase/Configurations/SiteConfiguration.cs b/src/ScadaLink.ConfigurationDatabase/Configurations/SiteConfiguration.cs index 66e4da0..abfe7bb 100644 --- a/src/ScadaLink.ConfigurationDatabase/Configurations/SiteConfiguration.cs +++ b/src/ScadaLink.ConfigurationDatabase/Configurations/SiteConfiguration.cs @@ -43,9 +43,16 @@ public class DataConnectionConfiguration : IEntityTypeConfiguration d.Configuration) + builder.Property(d => d.PrimaryConfiguration) .HasMaxLength(4000); + builder.Property(d => d.BackupConfiguration) + .HasMaxLength(4000); + + builder.Property(d => d.FailoverRetryCount) + .IsRequired() + .HasDefaultValue(3); + builder.HasOne() .WithMany() .HasForeignKey(d => d.SiteId) diff --git a/src/ScadaLink.DeploymentManager/ArtifactDeploymentService.cs b/src/ScadaLink.DeploymentManager/ArtifactDeploymentService.cs index a653a62..b622ce7 100644 --- a/src/ScadaLink.DeploymentManager/ArtifactDeploymentService.cs +++ b/src/ScadaLink.DeploymentManager/ArtifactDeploymentService.cs @@ -102,7 +102,7 @@ public class ArtifactDeploymentService // Map data connections var dataConnectionArtifacts = dataConnections.Select(dc => - new DataConnectionArtifact(dc.Name, dc.Protocol, dc.Configuration)).ToList(); + new DataConnectionArtifact(dc.Name, dc.Protocol, dc.PrimaryConfiguration, dc.BackupConfiguration, dc.FailoverRetryCount)).ToList(); // Map SMTP configurations — use Host as the artifact name (matches SQLite PK on site) var smtpArtifacts = smtpConfigurations.Select(smtp => diff --git a/src/ScadaLink.ManagementService/ManagementActor.cs b/src/ScadaLink.ManagementService/ManagementActor.cs index 34a04e4..435be26 100644 --- a/src/ScadaLink.ManagementService/ManagementActor.cs +++ b/src/ScadaLink.ManagementService/ManagementActor.cs @@ -689,7 +689,7 @@ public class ManagementActor : ReceiveActor private static async Task HandleCreateDataConnection(IServiceProvider sp, CreateDataConnectionCommand cmd, string user) { var repo = sp.GetRequiredService(); - var conn = new DataConnection(cmd.Name, cmd.Protocol, cmd.SiteId) { Configuration = cmd.Configuration }; + var conn = new DataConnection(cmd.Name, cmd.Protocol, cmd.SiteId) { PrimaryConfiguration = cmd.PrimaryConfiguration }; await repo.AddDataConnectionAsync(conn); await repo.SaveChangesAsync(); await AuditAsync(sp, user, "Create", "DataConnection", conn.Id.ToString(), conn.Name, conn); @@ -703,7 +703,7 @@ public class ManagementActor : ReceiveActor ?? throw new InvalidOperationException($"DataConnection with ID {cmd.DataConnectionId} not found."); conn.Name = cmd.Name; conn.Protocol = cmd.Protocol; - conn.Configuration = cmd.Configuration; + conn.PrimaryConfiguration = cmd.PrimaryConfiguration; await repo.UpdateDataConnectionAsync(conn); await repo.SaveChangesAsync(); await AuditAsync(sp, user, "Update", "DataConnection", conn.Id.ToString(), conn.Name, conn); diff --git a/src/ScadaLink.SiteRuntime/Actors/DeploymentManagerActor.cs b/src/ScadaLink.SiteRuntime/Actors/DeploymentManagerActor.cs index d8513ac..40e2b1f 100644 --- a/src/ScadaLink.SiteRuntime/Actors/DeploymentManagerActor.cs +++ b/src/ScadaLink.SiteRuntime/Actors/DeploymentManagerActor.cs @@ -615,7 +615,7 @@ public class DeploymentManagerActor : ReceiveActor, IWithTimers foreach (var dc in command.DataConnections) { await _storage.StoreDataConnectionDefinitionAsync( - dc.Name, dc.Protocol, dc.ConfigurationJson); + dc.Name, dc.Protocol, dc.PrimaryConfigurationJson); } } diff --git a/src/ScadaLink.SiteRuntime/Actors/SiteReplicationActor.cs b/src/ScadaLink.SiteRuntime/Actors/SiteReplicationActor.cs index ba3e736..1b79f26 100644 --- a/src/ScadaLink.SiteRuntime/Actors/SiteReplicationActor.cs +++ b/src/ScadaLink.SiteRuntime/Actors/SiteReplicationActor.cs @@ -185,7 +185,7 @@ public class SiteReplicationActor : ReceiveActor if (command.DataConnections != null) foreach (var dc in command.DataConnections) - await _storage.StoreDataConnectionDefinitionAsync(dc.Name, dc.Protocol, dc.ConfigurationJson); + await _storage.StoreDataConnectionDefinitionAsync(dc.Name, dc.Protocol, dc.PrimaryConfigurationJson); if (command.SmtpConfigurations != null) foreach (var smtp in command.SmtpConfigurations) diff --git a/src/ScadaLink.TemplateEngine/Flattening/FlatteningService.cs b/src/ScadaLink.TemplateEngine/Flattening/FlatteningService.cs index cad7615..7a2596c 100644 --- a/src/ScadaLink.TemplateEngine/Flattening/FlatteningService.cs +++ b/src/ScadaLink.TemplateEngine/Flattening/FlatteningService.cs @@ -90,7 +90,7 @@ public class FlatteningService connections[attr.BoundDataConnectionName] = new ConnectionConfig { Protocol = conn.Protocol, - ConfigurationJson = conn.Configuration + ConfigurationJson = conn.PrimaryConfiguration }; } } diff --git a/src/ScadaLink.TemplateEngine/Services/SiteService.cs b/src/ScadaLink.TemplateEngine/Services/SiteService.cs index e1225a5..51cf7f7 100644 --- a/src/ScadaLink.TemplateEngine/Services/SiteService.cs +++ b/src/ScadaLink.TemplateEngine/Services/SiteService.cs @@ -104,7 +104,7 @@ public class SiteService if (string.IsNullOrWhiteSpace(protocol)) return Result.Failure("Protocol is required."); - var connection = new DataConnection(name, protocol, siteId) { Configuration = configuration }; + var connection = new DataConnection(name, protocol, siteId) { PrimaryConfiguration = configuration }; await _repository.AddDataConnectionAsync(connection, cancellationToken); await _repository.SaveChangesAsync(cancellationToken); @@ -124,7 +124,7 @@ public class SiteService connection.Name = name; connection.Protocol = protocol; - connection.Configuration = configuration; + connection.PrimaryConfiguration = configuration; await _repository.UpdateDataConnectionAsync(connection, cancellationToken); await _repository.SaveChangesAsync(cancellationToken); diff --git a/tests/ScadaLink.TemplateEngine.Tests/Flattening/FlatteningServiceTests.cs b/tests/ScadaLink.TemplateEngine.Tests/Flattening/FlatteningServiceTests.cs index 32562c1..941e9cb 100644 --- a/tests/ScadaLink.TemplateEngine.Tests/Flattening/FlatteningServiceTests.cs +++ b/tests/ScadaLink.TemplateEngine.Tests/Flattening/FlatteningServiceTests.cs @@ -200,7 +200,7 @@ public class FlatteningServiceTests var connections = new Dictionary { - [100] = new("OPC-Server1", "OpcUa", 1) { Id = 100, Configuration = "opc.tcp://localhost:4840" } + [100] = new("OPC-Server1", "OpcUa", 1) { Id = 100, PrimaryConfiguration = "opc.tcp://localhost:4840" } }; var result = _sut.Flatten(