feat(historian): typed HistorianValue contracts, IHistorianDataUpdater, IHistorianBackendProvider (no behaviour change)
Claude-Session: https://claude.ai/code/session_01Ef2ozxS7QPanGT8uoATbTe
This commit is contained in:
@@ -524,7 +524,8 @@ public sealed class ContinuousHistorizationRecorder : ReceiveActor, IWithTimers
|
||||
+ "resuming value capture.");
|
||||
}
|
||||
|
||||
if (!TryCoerceToDouble(msg.Value, out double numeric))
|
||||
if (!(HistorianValue.TryFromObject(msg.Value, out HistorianValue historianValue)
|
||||
&& historianValue.TryGetDouble(out double numeric)))
|
||||
{
|
||||
// The SQL analog live-value path is numeric-only — drop non-coercible values (string/null/
|
||||
// reference) and meter the drop. No value content is logged.
|
||||
@@ -548,8 +549,8 @@ public sealed class ContinuousHistorizationRecorder : ReceiveActor, IWithTimers
|
||||
entries.Add(new HistorizationOutboxEntry(
|
||||
Guid.NewGuid(),
|
||||
historianName,
|
||||
numeric,
|
||||
GoodQuality,
|
||||
HistorianValue.FromDouble(numeric),
|
||||
0u, // Good
|
||||
timestampUtc));
|
||||
}
|
||||
}
|
||||
@@ -689,7 +690,7 @@ public sealed class ContinuousHistorizationRecorder : ReceiveActor, IWithTimers
|
||||
.Select(g => (
|
||||
Key: g.Key,
|
||||
Values: (IReadOnlyList<HistorizationValue>)g
|
||||
.Select(e => new HistorizationValue(e.TimestampUtc, e.NumericValue, e.Quality))
|
||||
.Select(e => new HistorizationValue(e.TimestampUtc, e.Value, e.StatusCode))
|
||||
.ToList()))
|
||||
.ToList();
|
||||
|
||||
@@ -879,24 +880,4 @@ public sealed class ContinuousHistorizationRecorder : ReceiveActor, IWithTimers
|
||||
}
|
||||
|
||||
private static TimeSpan Min(TimeSpan a, TimeSpan b) => a < b ? a : b;
|
||||
|
||||
private static bool TryCoerceToDouble(object? value, out double result)
|
||||
{
|
||||
switch (value)
|
||||
{
|
||||
case double d: result = d; return true;
|
||||
case float f: result = f; return true;
|
||||
case int i: result = i; return true;
|
||||
case long l: result = l; return true;
|
||||
case short s: result = s; return true;
|
||||
case ushort us: result = us; return true;
|
||||
case uint ui: result = ui; return true;
|
||||
case ulong ul: result = ul; return true;
|
||||
case byte b: result = b; return true;
|
||||
case sbyte sb: result = sb; return true;
|
||||
case decimal dec: result = (double)dec; return true;
|
||||
case bool bo: result = bo ? 1d : 0d; return true; // Boolean historizes as Int1 (1/0).
|
||||
default: result = 0d; return false; // string / null / DateTime / reference -> dropped.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
using ZB.MOM.WW.OtOpcUa.Core.Abstractions;
|
||||
using ZB.MOM.WW.OtOpcUa.Core.AlarmHistorian;
|
||||
|
||||
namespace ZB.MOM.WW.OtOpcUa.Runtime.Historian;
|
||||
|
||||
/// <summary>
|
||||
/// Builds the historian seams (read, live-value write, alarm write, provisioning, optional update)
|
||||
/// for one historian backend. The Host selects a provider by <see cref="Name"/>.
|
||||
/// </summary>
|
||||
public interface IHistorianBackendProvider
|
||||
{
|
||||
/// <summary>Backend name, e.g. "Gateway" or "SqlServer".</summary>
|
||||
string Name { get; }
|
||||
|
||||
/// <summary>The value kinds this backend can store; the recorder drops other kinds at capture.</summary>
|
||||
IReadOnlySet<HistorianValueKind> SupportedKinds { get; }
|
||||
|
||||
/// <summary>Creates the HistoryRead data source.</summary>
|
||||
/// <param name="sp">Service provider.</param>
|
||||
/// <returns>The data source.</returns>
|
||||
IHistorianDataSource CreateDataSource(IServiceProvider sp);
|
||||
|
||||
/// <summary>Creates the continuous-historization live-value writer.</summary>
|
||||
/// <param name="sp">Service provider.</param>
|
||||
/// <returns>The value writer.</returns>
|
||||
IHistorianValueWriter CreateValueWriter(IServiceProvider sp);
|
||||
|
||||
/// <summary>Creates the alarm-history writer.</summary>
|
||||
/// <param name="sp">Service provider.</param>
|
||||
/// <returns>The alarm writer.</returns>
|
||||
IAlarmHistorianWriter CreateAlarmWriter(IServiceProvider sp);
|
||||
|
||||
/// <summary>Creates the tag auto-provisioning hook.</summary>
|
||||
/// <param name="sp">Service provider.</param>
|
||||
/// <returns>The provisioning hook.</returns>
|
||||
IHistorianProvisioning CreateProvisioning(IServiceProvider sp);
|
||||
|
||||
/// <summary>Creates the HistoryUpdate capability.</summary>
|
||||
/// <param name="sp">Service provider.</param>
|
||||
/// <returns>The updater, or <c>null</c> when the backend does not support HistoryUpdate.</returns>
|
||||
IHistorianDataUpdater? CreateUpdater(IServiceProvider sp);
|
||||
}
|
||||
Reference in New Issue
Block a user