Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix reconnect issues for heartbeat, cyclic read and conditions #2353

Merged
merged 9 commits into from
Oct 6, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 8 additions & 2 deletions docs/opc-publisher/directmethods.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,15 @@

[Home](./readme.md)

OPC Publisher version 2.8.2 and later implements [IoT Hub Direct Methods](https://docs.microsoft.com/azure/iot-hub/iot-hub-devguide-direct-methods), which can be called from an application using the [IoT Hub Device SDK](https://docs.microsoft.com/azure/iot-hub/iot-hub-devguide-sdks).
For large-scale deployments, automating the configuration and management of OPC Publisher is critical. OPC Publisher version 2.8.2 and later implements [IoT Hub Direct Methods](https://docs.microsoft.com/azure/iot-hub/iot-hub-devguide-direct-methods), which can be called from an application using the [IoT Hub Device SDK](https://docs.microsoft.com/azure/iot-hub/iot-hub-devguide-sdks).

The following direct methods are exposed:
Azure IoT Hub's Cloud-to-Device (C2D) commands allow you to remotely configure and control OPC Publisher instances running on IoT Edge devices. For example, you can send commands to update the configuration, restart the module, or change runtime parameters without needing to manually intervene on each device. An example of sending a C2D command to update the configuration:

```bash
az iot hub invoke-module-method --hub-name <your-iot-hub> --device-id <your-device-id> --module-name <opc-publisher> --method-name SetConfiguredEndpoints --method-payload '{"Endpoints": [{"EndpointUrl": "opc.tcp://new-opc-server:4840", "OpcNodes": [{"Id": "ns=2;i=10853"}]}]}'
```

The following direct methods and many more can be used to remotely configure the OPC Publisher:

- [PublishNodes\_V1](#publishnodes_v1)
- [AddOrUpdateEndpoints\_V1](#addorupdateendpoints_v1)
Expand Down
4 changes: 4 additions & 0 deletions docs/opc-publisher/readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -370,6 +370,10 @@ The simplest way to configure OPC Publisher is via a file. A basic configuration
]
```
This configuration can be placed in a JSON file, typically named publishednodes.json, and provided to OPC Publisher using the [command line](./commandline.md) argument `-f, --pf, --publishfile`, e.g. `--pf=/app/publishednodes.json`.
> Environment variables can also be used to configure OPC Publisher. This method is particularly useful when deploying at scale or in environments where you want to externalize configuration from the container image. An example is `PublishedNodesFile`.
Example configuration files are [here](publishednodes_2.5.json?raw=1) and [here](publishednodes_2.8.json?raw=1).
### Configuration Schema
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="Azure.Identity" Version="1.12.1" />
<PackageReference Include="Azure.Core" Version="1.43.0" />
<PackageReference Include="Azure.Core" Version="1.44.0" />
<PackageReference Include="Azure.Messaging.EventHubs" Version="5.11.5" />
<PackageReference Include="Azure.ResourceManager.ContainerInstance" Version="1.2.1" />
<PackageReference Include="Azure.ResourceManager.Storage" Version="1.3.0" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Furly.Extensions.Abstractions" Version="1.0.68" />
<PackageReference Include="Furly.Extensions.Abstractions" Version="1.0.69" />
<PackageReference Include="System.Private.Uri" Version="4.3.2" />
</ItemGroup>
<ItemGroup>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,9 @@
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
</PackageReference>
<PackageReference Include="Furly.Extensions.Json" Version="1.0.68" />
<PackageReference Include="Furly.Extensions.MessagePack" Version="1.0.68" />
<PackageReference Include="Furly.Extensions.Newtonsoft" Version="1.0.68" />
<PackageReference Include="Furly.Extensions.Json" Version="1.0.69" />
<PackageReference Include="Furly.Extensions.MessagePack" Version="1.0.69" />
<PackageReference Include="Furly.Extensions.Newtonsoft" Version="1.0.69" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\src\Azure.IIoT.OpcUa.Publisher.Models.csproj" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@
<TieredPGO>true</TieredPGO>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Furly.Azure.IoT" Version="1.0.68" />
<PackageReference Include="Furly.Azure.KeyVault" Version="1.0.68" />
<PackageReference Include="Furly.Azure.IoT" Version="1.0.69" />
<PackageReference Include="Furly.Azure.KeyVault" Version="1.0.69" />
<PackageReference Include="System.IO.FileSystem" Version="4.3.0" />
<PackageReference Include="System.Net.Primitives" Version="4.3.1" />
</ItemGroup>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,15 +33,15 @@
<None Remove="pki\**" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Furly.Extensions.AspNetCore" Version="1.0.68" />
<PackageReference Include="Furly.Extensions.Mqtt" Version="1.0.68" />
<PackageReference Include="Furly.Extensions.Dapr" Version="1.0.68" />
<PackageReference Include="Furly.Extensions.MessagePack" Version="1.0.68" />
<PackageReference Include="Furly.Azure.EventHubs" Version="1.0.68" />
<PackageReference Include="Furly.Azure.IoT" Version="1.0.68" />
<PackageReference Include="Furly.Extensions.AspNetCore" Version="1.0.69" />
<PackageReference Include="Furly.Extensions.Mqtt" Version="1.0.69" />
<PackageReference Include="Furly.Extensions.Dapr" Version="1.0.69" />
<PackageReference Include="Furly.Extensions.MessagePack" Version="1.0.69" />
<PackageReference Include="Furly.Azure.EventHubs" Version="1.0.69" />
<PackageReference Include="Furly.Azure.IoT" Version="1.0.69" />
<PackageReference Include="Azure.Identity" Version="1.12.1" />
<PackageReference Include="Azure.Core" Version="1.43.0" />
<PackageReference Include="Furly.Tunnel" Version="1.0.68" />
<PackageReference Include="Azure.Core" Version="1.44.0" />
<PackageReference Include="Furly.Tunnel" Version="1.0.69" />
<PackageReference Include="Mono.Options" Version="6.12.0.148" />
<PackageReference Include="OpenTelemetry.Extensions.Hosting" Version="1.9.0" />
<PackageReference Include="OpenTelemetry.Exporter.OpenTelemetryProtocol" Version="1.9.0" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,6 @@
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
</ItemGroup>
<ItemGroup>
<None Remove="Resources\CyclicRead.json" />
<None Remove="Resources\Heartbeat2.json" />
</ItemGroup>
<ItemGroup>
<Service Include="{82a7f48d-3b50-4b1e-b82e-3ada8210c358}" />
</ItemGroup>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -310,15 +310,18 @@ static void Add(List<JsonMessage> messages, JsonElement item, ref JsonMessage? m
/// <param name="arguments"></param>
/// <param name="version"></param>
/// <param name="reverseConnectPort"></param>
/// <param name="keepAliveInterval"></param>
/// <param name="securityMode"></param>
protected void StartPublisher(string test, string publishedNodesFile = null,
string[] arguments = default, MqttVersion? version = null, int? reverseConnectPort = null)
string[] arguments = default, MqttVersion? version = null, int? reverseConnectPort = null,
int keepAliveInterval = 120, SecurityMode? securityMode = null)
{
var sw = Stopwatch.StartNew();
_logger = _logFactory.CreateLogger(test);

arguments ??= Array.Empty<string>();
_publishedNodesFilePath = Path.GetTempFileName();
WritePublishedNodes(test, publishedNodesFile, reverseConnectPort != null);
WritePublishedNodes(test, publishedNodesFile, reverseConnectPort != null, securityMode);

arguments = arguments.Concat(
new[]
Expand All @@ -341,7 +344,7 @@ protected void StartPublisher(string test, string publishedNodesFile = null,
}

_publisher = new PublisherModule(null, null, null, null,
_testOutputHelper, arguments, version);
_testOutputHelper, arguments, version, keepAliveInterval);
_logger.LogInformation("Publisher started in {Elapsed}.", sw.Elapsed);
}

Expand All @@ -351,13 +354,16 @@ protected void StartPublisher(string test, string publishedNodesFile = null,
/// <param name="test"></param>
/// <param name="publishedNodesFile"></param>
/// <param name="useReverseConnect"></param>
protected void WritePublishedNodes(string test, string publishedNodesFile, bool useReverseConnect = false)
/// <param name="securityMode"></param>
protected void WritePublishedNodes(string test, string publishedNodesFile, bool useReverseConnect = false,
SecurityMode? securityMode = null)
{
if (!string.IsNullOrEmpty(publishedNodesFile))
{
File.WriteAllText(_publishedNodesFilePath, File.ReadAllText(publishedNodesFile)
.Replace("\"{{UseReverseConnect}}\"", useReverseConnect ? "true" : "false", StringComparison.Ordinal)
.Replace("{{EndpointUrl}}", EndpointUrl, StringComparison.Ordinal)
.Replace("{{SecurityMode}}", (securityMode ?? SecurityMode.None).ToString(), StringComparison.Ordinal)
.Replace("{{DataSetWriterGroup}}", test, StringComparison.Ordinal));
}
}
Expand Down Expand Up @@ -393,14 +399,16 @@ protected async Task StopPublisherAsync()
/// <param name="test"></param>
/// <param name="publishedNodesFile"></param>
/// <param name="useReverseConnect"></param>
/// <param name="securityMode"></param>
/// <returns></returns>
protected PublishedNodesEntryModel[] GetEndpointsFromFile(string test, string publishedNodesFile,
bool useReverseConnect = false)
bool useReverseConnect = false, SecurityMode? securityMode = null)
{
IJsonSerializer serializer = new NewtonsoftJsonSerializer();
var fileContent = File.ReadAllText(publishedNodesFile)
.Replace("\"{{UseReverseConnect}}\"", useReverseConnect ? "true" : "false", StringComparison.Ordinal)
.Replace("{{EndpointUrl}}", EndpointUrl, StringComparison.Ordinal)
.Replace("{{SecurityMode}}", (securityMode ?? SecurityMode.None).ToString(), StringComparison.Ordinal)
.Replace("{{DataSetWriterGroup}}", test, StringComparison.Ordinal);
return serializer.Deserialize<PublishedNodesEntryModel[]>(fileContent);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -102,9 +102,10 @@ public sealed class PublisherModule : WebApplicationFactory<ModuleStartup>, IHtt
/// <param name="testOutputHelper"></param>
/// <param name="arguments"></param>
/// <param name="version"></param>
/// <param name="keepAliveInterval"></param>
public PublisherModule(IMessageSink messageSink, IEnumerable<DeviceTwinModel> devices = null,
string deviceId = null, string moduleId = null, ITestOutputHelper testOutputHelper = null,
string[] arguments = default, MqttVersion? version = null)
string[] arguments = default, MqttVersion? version = null, int keepAliveInterval = 120)
{
_logFactory = testOutputHelper != null ? LogFactory.Create(testOutputHelper, Logging.Config) : null;
ClientContainer = CreateIoTHubSdkClientContainer(messageSink, testOutputHelper, devices, version);
Expand Down Expand Up @@ -163,7 +164,7 @@ public PublisherModule(IMessageSink messageSink, IEnumerable<DeviceTwinModel> de
$"--id={publisherId}",
$"--ec={edgeHubCs}",
$"--mqc={mqttCs}",
"--ki=90",
$"--ki={keepAliveInterval}",
"--aa"
}).ToArray();
if (OperatingSystem.IsLinux())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
{
"EndpointUrl": "{{EndpointUrl}}",
"UseReverseConnect": "{{UseReverseConnect}}",
"EndpointSecurityMode": "None",
"EndpointSecurityMode": "{{SecurityMode}}",
"DataSetWriterGroup": "{{DataSetWriterGroup}}",
"OpcNodes": [
{
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
[
{
"EndpointUrl": "{{EndpointUrl}}",
"EndpointSecurityMode": "{{SecurityMode}}",
"DataSetFetchDisplayNames": true,
"OpcNodes": [
{ "Id": "i=2271" },
{ "Id": "i=2254" },
{ "Id": "i=2255" }
]
}
]
Loading
Loading