Skip to content

Commit

Permalink
Fix several issues reported (#2103)
Browse files Browse the repository at this point in the history
Fixes for 

* #2090 
* #2091 (Updated documentation)
* #2096 
* #2097 
* #2098 
* #2100
  • Loading branch information
marcschier authored Nov 13, 2023
1 parent 14d6a65 commit 8d0d6f8
Show file tree
Hide file tree
Showing 47 changed files with 420 additions and 170 deletions.
4 changes: 2 additions & 2 deletions common.props
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,8 @@
<!--<TreatWarningsAsErrors>true</TreatWarningsAsErrors>-->
</PropertyGroup>
<ItemGroup Condition="$(NO_RCS) == ''">
<PackageReference Include="Roslynator.Analyzers" Version="4.5.0" PrivateAssets="All"/>
<PackageReference Include="Roslynator.Formatting.Analyzers" Version="4.5.0" PrivateAssets="All"/>
<PackageReference Include="Roslynator.Analyzers" Version="4.6.2" PrivateAssets="All"/>
<PackageReference Include="Roslynator.Formatting.Analyzers" Version="4.6.2" PrivateAssets="All"/>
</ItemGroup>
<!-- only create the SARIF files for the SDL build step in cloud builds -->
<PropertyGroup Condition="'$(NBGV_NugetPackageVersion)' != ''">
Expand Down
4 changes: 3 additions & 1 deletion docs/opc-publisher/readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -575,9 +575,11 @@ The `DataSetExtensionFields` object in the [configuration](#configuration-schema

Values are formatted using the extended OPC UA Variant [JSON format](#json-encoding). This encoding is compliant with OPC UA Part 6, however it also allows to use simple JSON types which will be interpreted as Variant values using a simple heuristic, mapping the best OPC UA type possible to it.

> IMPORTANT: Extension fields are only sent as part of key frame messages when using Pub Sub encoding. You must configure a key frame count for key frames to be sent as the default key frame count value is 0 and therefore key frames are disabled.

#### Status codes

The status code `value` is the integer received over the wire from the server (full one including all bits).
The status code `value` is the integer received over the wire from the server (full one including all bits).

StatusCode "Good" is defined as 0 in OPC UA, which is omitted in JSON encoding (as per Part 6). The `symbol` in the encoding is what OPC Publisher is looking up from the standard defined codes (using the code bits which are the 16 bits defining the error code part of the status code).

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,4 @@
<ItemGroup>
<PackageReference Include="Furly.Extensions.Abstractions" Version="0.4.61" />
</ItemGroup>
<ItemGroup>
<PackageReference Update="Roslynator.Analyzers" Version="4.6.1" />
<PackageReference Update="Roslynator.Formatting.Analyzers" Version="4.6.1" />
</ItemGroup>
</Project>
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,12 @@
<ItemGroup>
<PackageReference Include="AutoFixture" Version="4.18.0" />
<PackageReference Include="FluentAssertions" Version="6.12.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.7.2" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.8.0" />
<PackageReference Include="coverlet.msbuild" Version="6.0.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="xunit" Version="2.5.3" />
<PackageReference Include="xunit" Version="2.6.1" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.5.3">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
Expand All @@ -25,8 +25,4 @@
<ItemGroup>
<Service Include="{82a7f48d-3b50-4b1e-b82e-3ada8210c358}" />
</ItemGroup>
<ItemGroup>
<PackageReference Update="Roslynator.Analyzers" Version="4.6.1" />
<PackageReference Update="Roslynator.Formatting.Analyzers" Version="4.6.1" />
</ItemGroup>
</Project>
Original file line number Diff line number Diff line change
Expand Up @@ -57,8 +57,4 @@
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
</ItemGroup>
<ItemGroup>
<PackageReference Update="Roslynator.Analyzers" Version="4.6.1" />
<PackageReference Update="Roslynator.Formatting.Analyzers" Version="4.6.1" />
</ItemGroup>
</Project>
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@
<PackageReference Include="Furly.Extensions.Dapr" Version="0.4.61" />
<PackageReference Include="Furly.Extensions.MessagePack" Version="0.4.61" />
<PackageReference Include="Furly.Tunnel" Version="0.2.55" />
<PackageReference Include="Grpc.Net.Client" Version="2.58.0" />
<PackageReference Include="Grpc.Net.Client" Version="2.59.0" />
<PackageReference Include="Mono.Options" Version="6.12.0.148" />
<PackageReference Include="OpenTelemetry.Extensions.Hosting" Version="1.6.0" />
<PackageReference Include="OpenTelemetry.Exporter.OpenTelemetryProtocol" Version="1.6.0" />
Expand All @@ -49,8 +49,4 @@
<ItemGroup>
<ProjectReference Include="..\..\Azure.IIoT.OpcUa.Publisher\src\Azure.IIoT.OpcUa.Publisher.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Update="Roslynator.Analyzers" Version="4.6.1" />
<PackageReference Update="Roslynator.Formatting.Analyzers" Version="4.6.1" />
</ItemGroup>
</Project>
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,33 @@ public class PublisherController : IMethodController
/// <summary>
/// Support restarting the module
/// </summary>
/// <param name="logger"></param>
public PublisherController(ILogger logger)
/// <param name="apikey"></param>
/// <param name="certificate"></param>
/// <param name="process"></param>
public PublisherController(IProcessControl process, IApiKeyProvider apikey,
ISslCertProvider certificate)
{
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
_apikey = apikey;
_certificate = certificate;
_process = process;
}

/// <summary>
/// Get ApiKey to use when calling the HTTP API.
/// </summary>
/// <returns></returns>
public Task<string?> GetApiKeyAsync()
{
return Task.FromResult(_apikey.ApiKey);
}

/// <summary>
/// Get server certificate as PEM.
/// </summary>
/// <returns></returns>
public Task<string?> GetServerCertificateAsync()
{
return Task.FromResult(_certificate.Certificate?.ExportCertificatePem());
}

/// <summary>
Expand All @@ -36,19 +59,16 @@ public PublisherController(ILogger logger)
/// <exception cref="NotSupportedException"></exception>
public async Task ShutdownAsync(bool failFast = false)
{
_logger.LogInformation("Shutdown called.");
if (failFast)
{
Environment.FailFast("Shutdown was invoked remotely.");
}
else
if (!_process.Shutdown(failFast))
{
Environment.Exit(0);
// Should be gone now
await Task.Delay(TimeSpan.FromSeconds(10)).ConfigureAwait(false);
throw new NotSupportedException("Failed to invoke shutdown");
}
await Task.Delay(TimeSpan.FromSeconds(10)).ConfigureAwait(false);
throw new NotSupportedException("Failed to invoke shutdown");
}

private readonly ILogger _logger;
private readonly IApiKeyProvider _apikey;
private readonly ISslCertProvider _certificate;
private readonly IProcessControl _process;
}
}
10 changes: 5 additions & 5 deletions src/Azure.IIoT.OpcUa.Publisher.Module/src/Runtime/CommandLine.cs
Original file line number Diff line number Diff line change
Expand Up @@ -142,12 +142,12 @@ public CommandLine(string[] args)
(bool? b) => this[OpcUaSubscriptionConfig.EnableDataSetKeepAlivesKey] = b?.ToString() ?? "True" },
{ $"eip|immediatepublishing:|{OpcUaSubscriptionConfig.EnableImmediatePublishingKey}:",
"By default OPC Publisher will create a subscription with publishing disabled and only enable it after it has filled it with all configured monitored items. Use this setting to create the subscription with publishing already enabled.\nDefault: `False`.\n",
(bool? b) => this[OpcUaSubscriptionConfig.DisableDataSetMetaDataKey] = b?.ToString() ?? "True" },
(bool? b) => this[OpcUaSubscriptionConfig.EnableImmediatePublishingKey] = b?.ToString() ?? "True" },
{ $"msi|metadatasendinterval=|{OpcUaSubscriptionConfig.DefaultMetaDataUpdateTimeKey}=",
"Default value in milliseconds for the metadata send interval which determines in which interval metadata is sent.\nEven when disabled, metadata is still sent when the metadata version changes unless `--mm=*Samples` is set in which case this setting is ignored. Only valid for network message encodings. \nDefault: `0` which means periodic sending of metadata is disabled.\n",
(int i) => this[OpcUaSubscriptionConfig.DefaultMetaDataUpdateTimeKey] = TimeSpan.FromMilliseconds(i).ToString() },
{ $"dm|disablemetadata:|{OpcUaSubscriptionConfig.DisableDataSetMetaDataKey}:",
"Disables sending any metadata when metadata version changes. This setting can be used to also override the messaging profile's default support for metadata sending. \nDefault: `False` if the messaging profile selected supports sending metadata, `True` otherwise.\n",
"Disables sending any metadata when metadata version changes. This setting can be used to also override the messaging profile's default support for metadata sending.\nIt is recommended to disable sending metadata when more than 100 nodes are part of a data set.\nDefault: `False` if the messaging profile selected supports sending metadata and `--strict` is set, `True` otherwise.\n",
(bool? b) => this[OpcUaSubscriptionConfig.DisableDataSetMetaDataKey] = b?.ToString() ?? "True" },
{ $"lc|legacycompatibility=|{LegacyCompatibility}=",
"Run the publisher in legacy (2.5.x) compatibility mode.\nDefault: `False` (disabled).\n",
Expand Down Expand Up @@ -316,13 +316,13 @@ public CommandLine(string[] args)
"The interval in seconds after which the publisher re-applies the desired state of the subscription to a session.\nDefault: `never` (only on configuration change).\n",
(int i) => this[OpcUaClientConfig.SubscriptionManagementIntervalKey] = TimeSpan.FromSeconds(i).ToString() },
{ $"bnr|badnoderetrydelay=|{OpcUaClientConfig.BadMonitoredItemRetryDelayKey}=",
$"The delay in seconds after which nodes that were rejected by the server while added or updating a subscription or while publishing, are re-applied to a subscription.\nDefault: `{OpcUaClientConfig.BadMonitoredItemRetryDelayDefaultSec}` seconds.\n",
$"The delay in seconds after which nodes that were rejected by the server while added or updating a subscription or while publishing, are re-applied to a subscription.\nSet to 0 to disable retrying.\nDefault: `{OpcUaClientConfig.BadMonitoredItemRetryDelayDefaultSec}` seconds.\n",
(int i) => this[OpcUaClientConfig.BadMonitoredItemRetryDelayKey] = TimeSpan.FromSeconds(i).ToString() },
{ $"inr|invalidnoderetrydelay=|{OpcUaClientConfig.InvalidMonitoredItemRetryDelayKey}=",
$"The delay in seconds after which the publisher attempts to re-apply nodes that were incorrectly configured to a subscription.\nDefault: `{OpcUaClientConfig.InvalidMonitoredItemRetryDelayDefaultSec}` seconds.\n",
$"The delay in seconds after which the publisher attempts to re-apply nodes that were incorrectly configured to a subscription.\nSet to 0 to disable retrying.\nDefault: `{OpcUaClientConfig.InvalidMonitoredItemRetryDelayDefaultSec}` seconds.\n",
(int i) => this[OpcUaClientConfig.InvalidMonitoredItemRetryDelayKey] = TimeSpan.FromSeconds(i).ToString() },
{ $"ser|subscriptionerrorretrydelay=|{OpcUaClientConfig.SubscriptionErrorRetryDelayKey}=",
$"The delay in seconds between attempts to create a subscription in a session.\nDefault: `{OpcUaClientConfig.SubscriptionErrorRetryDelayDefaultSec}` seconds.\n",
$"The delay in seconds between attempts to create a subscription in a session.\nSet to 0 to disable retrying.\nDefault: `{OpcUaClientConfig.SubscriptionErrorRetryDelayDefaultSec}` seconds.\n",
(int i) => this[OpcUaClientConfig.SubscriptionErrorRetryDelayKey] = TimeSpan.FromSeconds(i).ToString() },
{ $"dcp|disablecomplextypepreloading:|{OpcUaClientConfig.DisableComplexTypePreloadingKey}:",
"Complex types (structures, enumerations) a server exposes are preloaded from the server after the session is connected. In some cases this can cause problems either on the client or server itself. Use this setting to disable pre-loading support.\nNote that since the complex type system is used for meta data messages it will still be loaded at the time the subscription is created, therefore also disable meta data support if you want to ensure the complex types are never loaded for an endpoint.\nDefault: `false`.\n",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,8 @@ public static void AddPublisherServices(this ContainerBuilder builder)
builder.RegisterType<Router>()
.AsImplementedInterfaces();

builder.RegisterType<PublisherController>()
.AsImplementedInterfaces();
builder.RegisterType<ConfigurationController>()
.AsImplementedInterfaces();
builder.RegisterType<GeneralController>()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@
<PackageReference Include="Json.More.Net" Version="1.9.1" />
<PackageReference Include="FluentAssertions" Version="6.12.0" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" Version="7.0.13" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.7.2" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.8.0" />
<PackageReference Include="Moq" Version="[4.20.2]" />
<PackageReference Include="Divergic.Logging.Xunit" Version="4.2.0" />
<PackageReference Include="xunit" Version="2.5.3" />
<PackageReference Include="xunit" Version="2.6.1" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.5.3">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
Expand Down Expand Up @@ -53,6 +53,9 @@
<None Update="Resources\RegisteredRead.json">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Update="Resources\SimpleEvents2.json">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Update="Resources\SimpleEvents.json">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
Expand All @@ -69,8 +72,4 @@
<ItemGroup>
<Service Include="{82a7f48d-3b50-4b1e-b82e-3ada8210c358}" />
</ItemGroup>
<ItemGroup>
<PackageReference Update="Roslynator.Analyzers" Version="4.6.1" />
<PackageReference Update="Roslynator.Formatting.Analyzers" Version="4.6.1" />
</ItemGroup>
</Project>
Original file line number Diff line number Diff line change
Expand Up @@ -323,6 +323,9 @@ public void ConfigureContainer(ContainerBuilder builder)
}
// Override client config
builder.RegisterInstance(_config).AsImplementedInterfaces();
// Override process control
builder.RegisterType<ExitOverride>()
.AsImplementedInterfaces().SingleInstance();
}

/// <summary>
Expand Down Expand Up @@ -462,6 +465,17 @@ private IContainer CreateIoTHubSdkClientContainer(IMessageSink messageSink = nul
return builder.Build();
}

/// <summary>
/// Mock exiting
/// </summary>
internal sealed class ExitOverride : IProcessControl
{
public bool Shutdown(bool failFast)
{
return true;
}
}

/// <summary>
/// Adapter for telemetry handler
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ public async Task CanSendDataItemToMqttBrokerTest()
// Act
var (metadata, messages) = await ProcessMessagesAndMetadataAsync(
nameof(CanSendDataItemToMqttBrokerTest), "./Resources/DataItems.json",
messageType: "ua-data", arguments: new string[] { "--mm=PubSub", "--mdt={TelemetryTopic}/metadatamessage" },
messageType: "ua-data", arguments: new string[] { "--mm=PubSub", "--mdt={TelemetryTopic}/metadatamessage", "--dm=False" },
version: MqttVersion.v311);

// Assert
Expand Down Expand Up @@ -125,7 +125,7 @@ public async Task CanEncodeWithoutReversibleEncodingTest(string publishedNodesFi
// Arrange
// Act
var (metadata, result) = await ProcessMessagesAndMetadataAsync(nameof(CanEncodeWithoutReversibleEncodingTest),
publishedNodesFile, messageType: "ua-data", arguments: new[] { "--mm=PubSub", "--me=Json" },
publishedNodesFile, messageType: "ua-data", arguments: new[] { "--mm=PubSub", "--me=Json", "--dm=false" },
version: MqttVersion.v5);

Assert.Single(result);
Expand Down Expand Up @@ -165,7 +165,7 @@ public async Task CanEncodeWithReversibleEncodingTest(string publishedNodesFile)
// Act
var (metadata, result) = await ProcessMessagesAndMetadataAsync(nameof(CanEncodeWithReversibleEncodingTest),
publishedNodesFile, TimeSpan.FromMinutes(2), 4, messageType: "ua-data",
arguments: new[] { "--mm=PubSub", "--me=JsonReversible" },
arguments: new[] { "--mm=PubSub", "--me=JsonReversible", "--dm=False" },
version: MqttVersion.v311);

var messages = result
Expand Down Expand Up @@ -294,7 +294,7 @@ public async Task CanSendPendingConditionsToMqttBrokerTest()
// Act
var (metadata, messages) = await ProcessMessagesAndMetadataAsync(nameof(CanSendPendingConditionsToMqttBrokerTest),
"./Resources/PendingAlarms.json", BasicPubSubIntegrationTests.GetAlarmCondition, messageType: "ua-data",
arguments: new string[] { "--mm=PubSub" }, version: MqttVersion.v311);
arguments: new string[] { "--mm=PubSub", "--dm=False" }, version: MqttVersion.v311);

// Assert
var evt = Assert.Single(messages);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
[
{
"EndpointUrl": "{{EndpointUrl}}",
"UseSecurity": false,
"DataSetWriterGroup": "{{DataSetWriterGroup}}",
"OpcNodes": [
{
"Id": "i=2253",
"DisplayName": "Alarm",
"EventFilter": {
"TypeDefinitionId": "i=10060"
}
},
{
"Id": "i=2253",
"DisplayName": "CycleStarted",
"EventFilter": {
"TypeDefinitionId": "ns=16;i=235"
}
}
]
}
]
Loading

0 comments on commit 8d0d6f8

Please sign in to comment.