Skip to content

Commit 0611e31

Browse files
Merge pull request #508 from GridProtectionAlliance/route-dnp3-logs
Dnp3Adapters: Route log messages to the appropriate adapter
2 parents fbfa6ba + e0b471b commit 0611e31

3 files changed

Lines changed: 36 additions & 15 deletions

File tree

Source/Libraries/Adapters/Dnp3Adapters/Dnp3InputAdapter.cs

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,16 @@ public double PollingInterval
173173
set => m_pollingInterval = TimeSpan.FromSeconds(value);
174174
}
175175

176+
/// <inheritdoc/>
177+
public string ChannelID
178+
{
179+
get
180+
{
181+
TcpClientConfig tcpConfig = m_masterConfig.client;
182+
return $"{tcpConfig.address}:{tcpConfig.port}";
183+
}
184+
}
185+
176186
/// <summary>
177187
/// Gets the flag indicating if this adapter supports temporal processing.
178188
/// </summary>
@@ -311,17 +321,16 @@ public override void Initialize()
311321
protected override void AttemptConnection()
312322
{
313323
TcpClientConfig tcpConfig = m_masterConfig.client;
314-
string endPoint = $"{tcpConfig.address}:{tcpConfig.port}";
315324
TimeSpan minRetry = TimeSpan.FromMilliseconds(tcpConfig.minRetryMs);
316325
TimeSpan maxRetry = TimeSpan.FromMilliseconds(tcpConfig.maxRetryMs);
317326
TimeSpan reconnectDelay = TimeSpan.FromMilliseconds(tcpConfig.reconnectDelayMs);
318327
ChannelRetry channelRetry = new(minRetry, maxRetry, reconnectDelay);
319-
IChannelListener channelListener = new ChannelListener(state => OnStatusMessage(MessageLevel.Info, $"{endPoint} - Channel state change: {state}"));
328+
IChannelListener channelListener = new ChannelListener(state => OnStatusMessage(MessageLevel.Info, $"{ChannelID} - Channel state change: {state}"));
320329

321-
IChannel channel = s_manager.AddTCPClient(endPoint, tcpConfig.level, channelRetry, [new IPEndpoint(tcpConfig.address, tcpConfig.port)], channelListener);
330+
IChannel channel = s_manager.AddTCPClient(ChannelID, tcpConfig.level, channelRetry, [new IPEndpoint(tcpConfig.address, tcpConfig.port)], channelListener);
322331
m_channel = channel;
323332

324-
IMaster master = channel.AddMaster(endPoint, m_soeHandler, DefaultMasterApplication.Instance, m_masterConfig.master);
333+
IMaster master = channel.AddMaster(ChannelID, m_soeHandler, DefaultMasterApplication.Instance, m_masterConfig.master);
325334

326335
if (m_pollingInterval > TimeSpan.Zero)
327336
master.AddClassScan(ClassField.AllClasses, m_pollingInterval, m_soeHandler, TaskConfig.Default);

Source/Libraries/Adapters/Dnp3Adapters/Dnp3OutputAdapter.cs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -431,6 +431,9 @@ public Dnp3OutputAdapter()
431431
/// <inheritdoc />
432432
public override bool OutputIsForArchive => false;
433433

434+
/// <inheritdoc/>
435+
public string ChannelID => $"gsf-dnp3-outstation:{Name}";
436+
434437
/// <inheritdoc />
435438
protected override bool UseAsyncConnect => false;
436439

@@ -582,7 +585,7 @@ protected override void AttemptConnection()
582585
{
583586
// TCP server channel (listener)
584587
m_channel = s_manager.AddTCPServer(
585-
$"gsf-dnp3-outstation:{Name}",
588+
ChannelID,
586589
getLogLevel(),
587590
ServerAcceptMode.CloseExisting,
588591
new IPEndpoint(Interface, Port),
@@ -622,7 +625,7 @@ protected override void AttemptConnection()
622625

623626
// Create a GSF outstation application that includes adapter logging
624627
m_outstation = m_channel.AddOutstation(
625-
$"gsf-dnp3-outstation:{Name}",
628+
ChannelID,
626629
new ReadOnlyCommandHandler(),
627630
new GSFOutstationApplication(this),
628631
outstationStackConfig

Source/Libraries/Adapters/Dnp3Adapters/IaonProxyLogHandler.cs

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using System;
22
using System.Collections.Generic;
3+
using System.Linq;
34
using System.Text;
45
using Automatak.DNP3.Interface;
56
using GSF.Diagnostics;
@@ -14,6 +15,11 @@ namespace DNP3Adapters;
1415
/// </summary>
1516
public interface IDnp3Adapter : IAdapter
1617
{
18+
/// <summary>
19+
/// Gets the ID of the channel added to the DNP3 manager.
20+
/// </summary>
21+
public string ChannelID { get; }
22+
1723
/// <summary>
1824
/// Raises the <see cref="IAdapter.ProcessException"/> event.
1925
/// </summary>
@@ -34,7 +40,7 @@ internal class IaonProxyLogHandler<T> : ILogHandler where T : IDnp3Adapter
3440
/// <summary>
3541
/// Gets the static adapters that are currently registered with this proxy.
3642
/// </summary>
37-
public List<IDnp3Adapter> Adapters { get; } = [];
43+
public Dictionary<string, IDnp3Adapter> Adapters { get; } = [];
3844

3945
/// <summary>
4046
/// Gets the static status proxy that is used to process exceptions and status messages.
@@ -50,7 +56,7 @@ public void RegisterAdapter(T adapter)
5056
lock (Adapters)
5157
{
5258
// Add adapter to list of available adapters
53-
Adapters.Add(adapter);
59+
Adapters.Add(adapter.ChannelID, adapter);
5460

5561
// If no adapter has been designated as the status proxy, assign this one
5662
StatusProxy ??= adapter;
@@ -66,13 +72,13 @@ public void UnregisterAdapter(T adapter)
6672
lock (Adapters)
6773
{
6874
// Remove this adapter from the available list
69-
Adapters.Remove(adapter);
75+
Adapters.Remove(adapter.ChannelID);
7076

7177
// See if we are disposing the status proxy instance
7278
if (ReferenceEquals(StatusProxy, adapter))
7379
{
7480
// Attempt to find a new status proxy
75-
StatusProxy = Adapters.Count > 0 ? Adapters[0] : null;
81+
StatusProxy = Adapters.Values.FirstOrDefault();
7682
}
7783
}
7884
}
@@ -87,26 +93,29 @@ public void Log(LogEntry entry)
8793
// contends with adapter initialization and disposal so contention will not be the normal case
8894
lock (Adapters)
8995
{
90-
if (StatusProxy is null || StatusProxy.IsDisposed)
96+
if (!Adapters.TryGetValue(entry.alias, out IDnp3Adapter? adapter))
97+
adapter = StatusProxy;
98+
99+
if (adapter is null || adapter.IsDisposed)
91100
return;
92101

93102
if ((entry.filter.Flags & LogFilters.ERROR) > 0)
94103
{
95104
// Expose errors through exception processor
96105
InvalidOperationException exception = new(FormatLogEntry(entry));
97-
StatusProxy.OnProcessException(MessageLevel.Error, exception);
106+
adapter.OnProcessException(MessageLevel.Error, exception);
98107
}
99108
else
100109
{
101110
// For other messages, we just expose as a normal status
102111
string message = FormatLogEntry(entry);
103112

104113
if ((entry.filter.Flags & LogFilters.WARNING) > 0)
105-
StatusProxy.OnStatusMessage(MessageLevel.Warning, message);
114+
adapter.OnStatusMessage(MessageLevel.Warning, message);
106115
else if ((entry.filter.Flags & LogFilters.DEBUG) > 0)
107-
StatusProxy.OnStatusMessage(MessageLevel.Debug, message);
116+
adapter.OnStatusMessage(MessageLevel.Debug, message);
108117
else
109-
StatusProxy.OnStatusMessage(MessageLevel.Info, message);
118+
adapter.OnStatusMessage(MessageLevel.Info, message);
110119
}
111120
}
112121
}

0 commit comments

Comments
 (0)