diff --git a/src/Gemstone.Timeseries/Adapters/AdapterCache.cs b/src/Gemstone.Timeseries/Adapters/AdapterCache.cs index 0bf0f8b95..b4c05011d 100644 --- a/src/Gemstone.Timeseries/Adapters/AdapterCache.cs +++ b/src/Gemstone.Timeseries/Adapters/AdapterCache.cs @@ -445,6 +445,18 @@ public static IEnumerable GetConnectionParameters(string as return GetConnectionParameters(assemblyName, typeName, connectionSettings?.ParseKeyValuePairs()); } + /// + /// Gets the AdapterProtocol attribute for the protocol acronym. + /// + /// + /// Adapter Protocl for the protocol acronym. + public static AdapterProtocolAttribute? GetProtocolAttribute(string acronym) + { + return AdapterProtocols.Values + .SelectMany(info => info.Attributes) + .FirstOrDefault(attr => string.Equals(attr.Acronym, acronym, StringComparison.OrdinalIgnoreCase)); + } + /// /// Gets the connection parameters for the adapter and type name, optionally applying connection settings. /// diff --git a/src/Gemstone.Timeseries/Adapters/AdapterCollectionBase.cs b/src/Gemstone.Timeseries/Adapters/AdapterCollectionBase.cs index fffe3202d..d3415d713 100644 --- a/src/Gemstone.Timeseries/Adapters/AdapterCollectionBase.cs +++ b/src/Gemstone.Timeseries/Adapters/AdapterCollectionBase.cs @@ -1204,6 +1204,13 @@ protected internal virtual void OnProcessException(MessageLevel level, Exception } } + private static string WrapItemMessage(T item, string message) => $"[{item.Name}] {message}"; + + private void OnItemStatusMessage(T item, MessageLevel level, string status, string? eventName = null, MessageFlags flags = MessageFlags.None) => + OnStatusMessage(level, WrapItemMessage(item, status), eventName, flags); + + private static InvalidOperationException WrapItemException(T item, string message, Exception exception) => new(WrapItemMessage(item, $"{message}: {exception.Message}"), exception); + /// /// Raises event. /// @@ -1327,8 +1334,7 @@ protected virtual void InitializeItem(T? item) catch (Exception ex) { // Process exception for logging - string errorMessage = $"Failed to queue initialize operation for adapter {item.Name}: {ex.Message}"; - OnProcessException(MessageLevel.Warning, new InvalidOperationException(errorMessage, ex)); + OnProcessException(MessageLevel.Warning, WrapItemException(item, "Failed to queue initialize operation", ex)); } } @@ -1373,7 +1379,11 @@ private void Initialize(T item) " try adjusting the initialization timeout to suppress this message during" + " normal operations."; - OnStatusMessage(MessageLevel.Warning, string.Format(MessageFormat, item.Name, item.InitializationTimeout / 1000.0), "Initialization"); + OnItemStatusMessage( + item, + MessageLevel.Warning, + string.Format(MessageFormat, item.Name, item.InitializationTimeout / 1000.0), + "Initialization"); // ReSharper disable once AccessToModifiedClosure cancelInitializationTimeout = initializationTimeoutAction?.DelayAndExecute(item.InitializationTimeout); @@ -1429,7 +1439,7 @@ private void Initialize(T item) catch (Exception ex) { // We report any errors encountered during initialization... - OnProcessException(MessageLevel.Warning, new InvalidOperationException($"Failed to initialize adapter {item.Name}: {ex.Message}", ex), "Initialization"); + OnProcessException(MessageLevel.Warning, WrapItemException(item, "Failed to initialize adapter", ex), "Initialization"); // Initialization failed, so stop the timeout timer cancelInitializationTimeout?.Invoke(); @@ -1449,7 +1459,7 @@ private void Start(T item) catch (Exception ex) { // We report any errors encountered during startup... - OnProcessException(MessageLevel.Warning, new InvalidOperationException($"Failed to start adapter {item.Name}: {ex.Message}", ex), "Startup"); + OnProcessException(MessageLevel.Warning, WrapItemException(item, "Failed to start adapter", ex), "Startup"); } } diff --git a/src/Gemstone.Timeseries/Adapters/AdapterProtocolAttribute.cs b/src/Gemstone.Timeseries/Adapters/AdapterProtocolAttribute.cs index ca2ec6d30..cb8ea773e 100644 --- a/src/Gemstone.Timeseries/Adapters/AdapterProtocolAttribute.cs +++ b/src/Gemstone.Timeseries/Adapters/AdapterProtocolAttribute.cs @@ -105,6 +105,21 @@ public class AdapterProtocolAttribute : Attribute public Application[] Applications { get; } + /// + /// Device fields locked when the device is external. Empty = nothing locked. + /// + public string[] LockedDeviceFields { get; } + + /// + /// Measurement fields locked when the measurement is external. Empty = nothing locked. + /// + public string[] LockedMeasurementFields { get; } + + /// + /// Phasor fields locked when the phasor is external. Empty = nothing locked. + /// + public string[] LockedPhasorFields { get; } + /// /// Creates a new instance of the class. /// @@ -115,7 +130,17 @@ public class AdapterProtocolAttribute : Attribute /// Determines if the adapter protocol supports a connection test. /// Load order of the adapter protocol. /// Applications that support this protocol in the UI. - public AdapterProtocolAttribute(string acronym, string name, ProtocolType type, UIVisibility visibility, bool supportsConnectionTest = true, int loadOrder = 0, Application[] applications = null) + public AdapterProtocolAttribute( + string acronym, + string name, + ProtocolType type, + UIVisibility visibility, + bool supportsConnectionTest = true, + int loadOrder = 0, + Application[] applications = null, + string[] lockedDeviceFields = null, + string[] lockedMeasurementFields = null, + string[] lockedPhasorFields = null) { ArgumentException.ThrowIfNullOrWhiteSpace(acronym); ArgumentException.ThrowIfNullOrWhiteSpace(name); @@ -127,6 +152,9 @@ public AdapterProtocolAttribute(string acronym, string name, ProtocolType type, SupportsConnectionTest = supportsConnectionTest; LoadOrder = loadOrder; Applications = applications ?? DefaultApplications; + LockedDeviceFields = lockedDeviceFields ?? Array.Empty(); + LockedMeasurementFields = lockedMeasurementFields ?? Array.Empty(); + LockedPhasorFields = lockedPhasorFields ?? Array.Empty(); } } diff --git a/src/Gemstone.Timeseries/ConcentratorBase.cs b/src/Gemstone.Timeseries/ConcentratorBase.cs index 80e3ffe93..020b1b51b 100644 --- a/src/Gemstone.Timeseries/ConcentratorBase.cs +++ b/src/Gemstone.Timeseries/ConcentratorBase.cs @@ -856,7 +856,7 @@ public virtual int ProcessingInterval [ConnectionStringParameter] [Label("Time Resolution")] [Category("Frame Timing")] - [DefaultValue(Ticks.PerMillisecond)] + [DefaultValueExpression("Settings.Instance[\"System\"][\"DefaultTimeResolution\"]")] [Description("Defines the maximum time resolution, in ticks, to use when sorting measurements by timestamps into their proper destination frame in Ticks.")] public long TimeResolution { diff --git a/src/Gemstone.Timeseries/ServiceHostBase.cs b/src/Gemstone.Timeseries/ServiceHostBase.cs index 526840fd2..5d4d35175 100644 --- a/src/Gemstone.Timeseries/ServiceHostBase.cs +++ b/src/Gemstone.Timeseries/ServiceHostBase.cs @@ -4219,6 +4219,7 @@ public static void DefineSettings(Settings settings, string settingsCategory = S section.DefaultFrameRate = (30, "Defines the default frame rate used for adapters in frames per second."); section.DefaultLeadTime = (10.0D, "Defines the default lead time used for adapters in seconds."); section.DefaultLagTime = (10.0D, "Defines the default lag time used for adapters in seconds."); + section.DefaultTimeResolution = (10000L, "Defines the default time resolution used for adapters in ticks"); // Classic with condensed phase information //private const string DefaultPointTagNameExpression = "{Company.Acronym}_{DeviceAcronym}[?{SignalType.Source}=Phasor[-{SignalType.Suffix}{SignalIndex}]]:[?{Vendor.Acronym}!=[{VendorAcronym}_]][?{Phase}=A[APH]][?{Phase}=B[BPH]][?{Phase}=C[CPH]][?{Phase}=+[PSQ]][?{Phase}=-[NSQ]][?{Phase}=0[ZSQ]]{SignalType.Abbreviation}[?{SignalType.Source}!=Phasor[?{SignalIndex}!=-1[{SignalIndex}]]]";