From a15b401397e6aec1480b20ba030c0c7726150f3e Mon Sep 17 00:00:00 2001 From: Daniele Colonna Date: Fri, 8 May 2020 10:41:14 +0200 Subject: [PATCH 1/3] Add publishing configuration to the engineering tool (#428) --- .../src/Models/ListNode.cs | 37 +++++ .../src/Pages/Browser.razor | 128 ++++++++++++++---- .../src/Pages/Endpoints.razor | 9 +- .../src/Pages/_DrawerPublisherContent.razor | 75 ++++++++++ .../src/Services/Publisher.cs | 7 +- 5 files changed, 220 insertions(+), 36 deletions(-) create mode 100644 samples/src/Microsoft.Azure.IIoT.App/src/Pages/_DrawerPublisherContent.razor diff --git a/samples/src/Microsoft.Azure.IIoT.App/src/Models/ListNode.cs b/samples/src/Microsoft.Azure.IIoT.App/src/Models/ListNode.cs index 797ef94741..203be025d2 100644 --- a/samples/src/Microsoft.Azure.IIoT.App/src/Models/ListNode.cs +++ b/samples/src/Microsoft.Azure.IIoT.App/src/Models/ListNode.cs @@ -4,6 +4,7 @@ // ------------------------------------------------------------ namespace Microsoft.Azure.IIoT.App.Services { + using System; using Microsoft.Azure.IIoT.OpcUa.Api.Core.Models; using Microsoft.Azure.IIoT.OpcUa.Api.Publisher.Models; using System.Collections.Generic; @@ -34,5 +35,41 @@ public ListNode() { public PublishedItemApiModel PublishedItem { get; set; } public bool Publishing { get; set; } + + /// + /// PublishingInterval + /// + public string RequestedPublishingInterval { + get => (PublishedItem?.PublishingInterval ?? TimeSpan.MinValue) + == TimeSpan.MinValue ? + null : PublishedItem.PublishingInterval.Value.TotalMilliseconds.ToString(); + set { + PublishedItem.PublishingInterval = string.IsNullOrWhiteSpace(value) ? TimeSpan.MinValue : TimeSpan.FromMilliseconds(Convert.ToDouble(value)); + } + } + + /// + /// SamplingInterval + /// + public string RequestedSamplingInterval { + get => (PublishedItem?.SamplingInterval ?? TimeSpan.MinValue) + == TimeSpan.MinValue ? + null : PublishedItem.SamplingInterval.Value.TotalMilliseconds.ToString(); + set { + PublishedItem.SamplingInterval = string.IsNullOrWhiteSpace(value) ? TimeSpan.MinValue : TimeSpan.FromMilliseconds(Convert.ToDouble(value)); + } + } + + /// + /// HeartbeatInterval + /// + public string RequestedHeartbeatInterval { + get => (PublishedItem?.HeartbeatInterval ?? TimeSpan.MinValue) + == TimeSpan.MinValue ? + null : PublishedItem.HeartbeatInterval.Value.TotalSeconds.ToString(); + set { + PublishedItem.HeartbeatInterval = string.IsNullOrWhiteSpace(value) ? TimeSpan.MinValue : TimeSpan.FromSeconds(Convert.ToDouble(value)); + } + } } } diff --git a/samples/src/Microsoft.Azure.IIoT.App/src/Pages/Browser.razor b/samples/src/Microsoft.Azure.IIoT.App/src/Pages/Browser.razor index a3987e4c68..6f673edb40 100644 --- a/samples/src/Microsoft.Azure.IIoT.App/src/Pages/Browser.razor +++ b/samples/src/Microsoft.Azure.IIoT.App/src/Pages/Browser.razor @@ -76,6 +76,7 @@ Node Type Value Publishing + Publishing Config @if (PagedNodeList.Results.Count > 0) { @@ -115,6 +116,7 @@ else if (node.NodeClass == NodeClass.Method) { + @{_drawerType = drawer.Action;} @node.NodeClass @@ -150,16 +152,36 @@ @if (node.NodeClass == NodeClass.Variable) { - - - @{string output = (node.Publishing == true && node.PublishedItem != null) ? $"On, sampling {node.PublishedItem?.SamplingInterval.Value.TotalMilliseconds} ms" : "Off";} - @output - + + @{string output = (node.Publishing == true) ? "On" : "Off";} + @output + + + @{_drawerType = drawer.Publisher;} + @if (node.Publishing == true && node.PublishedItem != null) + { + + @(node.PublishedItem.SamplingInterval?.TotalMilliseconds.ToString() ?? "1000") ms
+
+ + @(node.PublishedItem.PublishingInterval?.TotalMilliseconds.ToString() ?? "1000") ms
+
+ + @(node.PublishedItem.HeartbeatInterval == null ? "-" : node.PublishedItem.HeartbeatInterval?.TotalSeconds.ToString() + " sec") +
+
+ } + else + { + + } + } else { N/A + N/A } } @@ -170,7 +192,14 @@ - <_DrawerActionContent NodeData="@NodeData" EndpointId="@EndpointId" PagedNodeList="@PagedNodeList" Credential="@Credential"> + @if (_drawerType == drawer.Action) + { + <_DrawerActionContent NodeData="@NodeData" EndpointId="@EndpointId" PagedNodeList="@PagedNodeList" Credential="@Credential"> + } + else + { + <_DrawerPublisherContent NodeData="@NodeData" Onclick="((NodeData) => ClickHandler((ListNode)NodeData))"> + } await PagerPageChanged(Page)) /> @@ -204,6 +233,12 @@ private string _tableView = "visible"; private string _tableEmpty = "displayNone"; private List _parentId { get; set; } + private enum drawer + { + Action = 0, + Publisher + } + private drawer _drawerType { get; set; } /// @@ -332,41 +367,65 @@ CommonHelper.Spinner = ""; } + /// + /// Manage Publishing a node + /// + /// + /// + private async Task SetPublishingAsync(string endpointId, ListNode node, ChangeEventArgs ev) + { + if ((bool)ev?.Value) + { + await PublishNodeAsync(endpointId, node); + } + else + { + await UnPublishNodeAsync(endpointId, node); + } + } + /// /// Publish a node /// /// /// - private async Task SetPublishing(string endpointId, ListNode node, ChangeEventArgs ev) + private async Task PublishNodeAsync(string endpointId, ListNode node) { - if ((bool)ev.Value) + node.Publishing = true; + var publishingInterval = node.PublishedItem?.PublishingInterval == null ? TimeSpan.FromMilliseconds(1000) : node.PublishedItem.PublishingInterval; + var samplingInterval = node.PublishedItem?.SamplingInterval == null ? TimeSpan.FromMilliseconds(1000) : node.PublishedItem.SamplingInterval; + var heartbeatInterval = node.PublishedItem?.HeartbeatInterval; + var result = await Publisher.StartPublishingAsync(endpointId, node.Id, node.NodeName, samplingInterval, publishingInterval, heartbeatInterval, Credential); + if (result) { - var result = await Publisher.StartPublishingAsync(endpointId, node.Id, node.NodeName, 1000, 1000, Credential); - if (result) + node.PublishedItem = new OpcUa.Api.Publisher.Models.PublishedItemApiModel() { - node.PublishedItem = new OpcUa.Api.Publisher.Models.PublishedItemApiModel() - { - NodeId = node.Id, - DisplayName = node.NodeName, - PublishingInterval = TimeSpan.FromMilliseconds(1000), - SamplingInterval = TimeSpan.FromMilliseconds(1000) - }; - node.Publishing = true; - } - else - { - node.PublishedItem = null; - node.Publishing = false; - } + NodeId = node.Id, + DisplayName = node.NodeName, + PublishingInterval = publishingInterval, + SamplingInterval = samplingInterval, + HeartbeatInterval = heartbeatInterval + }; } else { - var result = await Publisher.StopPublishingAsync(endpointId, node.Id, Credential); - if (result) - { - node.PublishedItem = null; - node.Publishing = false; - } + node.PublishedItem = null; + node.Publishing = false; + } + } + + /// + /// UnPublish a node + /// + /// + /// + private async Task UnPublishNodeAsync(string endpointId, ListNode node) + { + var result = await Publisher.StopPublishingAsync(endpointId, node.Id, Credential); + if (result) + { + node.PublishedItem = null; + node.Publishing = false; } } @@ -409,6 +468,15 @@ return Task.CompletedTask; } + /// + /// ClickHandler + /// + async Task ClickHandler(ListNode node) + { + CloseDrawer(); + await PublishNodeAsync(EndpointId, node); + } + /// /// Dispose /// diff --git a/samples/src/Microsoft.Azure.IIoT.App/src/Pages/Endpoints.razor b/samples/src/Microsoft.Azure.IIoT.App/src/Pages/Endpoints.razor index 22cb6e7df7..e1b6971e0a 100644 --- a/samples/src/Microsoft.Azure.IIoT.App/src/Pages/Endpoints.razor +++ b/samples/src/Microsoft.Azure.IIoT.App/src/Pages/Endpoints.razor @@ -38,6 +38,7 @@ Security Policy Security Level Endpoint State + Toggle Endpoint Activation Status @@ -77,7 +78,8 @@ @endpoint.EndpointModel.Registration.Endpoint.Url } @endpoint.EndpointModel.Registration.Endpoint.SecurityMode - @endpoint.EndpointModel.Registration.Endpoint.SecurityPolicy + @{ var securityPolicy = endpoint.EndpointModel.Registration.Endpoint.SecurityPolicy[(endpoint.EndpointModel.Registration.Endpoint.SecurityPolicy.LastIndexOf("#") + 1)..endpoint.EndpointModel.Registration.Endpoint.SecurityPolicy.Length];} + @securityPolicy @endpoint.EndpointModel.Registration.SecurityLevel @(endpoint.EndpointModel.EndpointState?.ToString() ?? "Disconnected") @@ -93,7 +95,7 @@ } - @endpoint.EndpointModel.ActivationState.ToString() + } else { @@ -106,9 +108,10 @@ } - @endpoint.EndpointModel.ActivationState.ToString() + } + @endpoint.EndpointModel.ActivationState.ToString() Published Nodes diff --git a/samples/src/Microsoft.Azure.IIoT.App/src/Pages/_DrawerPublisherContent.razor b/samples/src/Microsoft.Azure.IIoT.App/src/Pages/_DrawerPublisherContent.razor new file mode 100644 index 0000000000..ed77c4e097 --- /dev/null +++ b/samples/src/Microsoft.Azure.IIoT.App/src/Pages/_DrawerPublisherContent.razor @@ -0,0 +1,75 @@ +@*------------------------------------------------------------ + Copyright (c) Microsoft Corporation. All rights reserved. + Licensed under the MIT License (MIT). See License.txt in the repo root for license information. +------------------------------------------------------------*@ + +@using Microsoft.Azure.IIoT.App.Services +@using Microsoft.AspNetCore.Components; +@using OpcUa.Api.Publisher.Models; + +@inject Registry RegistryHelper + +
+
+ Enter configuration parameters. +
To apply default config leave the input field blank and click on apply +
+
+
+
+ Node Id: +
@NodeData.Id
+

+
+ Display Name: +
@NodeData.NodeName
+

+
+
+
Publishing Interval ms
+ +

+
+
Sampling Interval ms
+ +

+
+
Heartbeat Interval sec
+ +

+
+
+ +
+
+
+
+ +@code { + [Parameter] + public ListNode NodeData { get; set; } + + [Parameter] + public EventCallback Onclick { get; set; } + + /// + /// OnInitialized + /// + protected override void OnInitialized() + { + if (NodeData.PublishedItem == null) + { + NodeData.PublishedItem = new PublishedItemApiModel(); + } + } + + + /// + /// Close Drawer and update discovery + /// + /// + private async Task UpdatePublishedNodeConfigAsync(ListNode node) + { + await Onclick.InvokeAsync(node); + } +} diff --git a/samples/src/Microsoft.Azure.IIoT.App/src/Services/Publisher.cs b/samples/src/Microsoft.Azure.IIoT.App/src/Services/Publisher.cs index adf60a6041..917733ef8f 100644 --- a/samples/src/Microsoft.Azure.IIoT.App/src/Services/Publisher.cs +++ b/samples/src/Microsoft.Azure.IIoT.App/src/Services/Publisher.cs @@ -76,15 +76,16 @@ public async Task> PublishedAsync(string endp /// /// ErrorStatus public async Task StartPublishingAsync(string endpointId, string nodeId, string displayName, - int samplingInterval, int publishingInterval, CredentialModel credential = null) { + TimeSpan? samplingInterval, TimeSpan? publishingInterval, TimeSpan? heartBeatInterval, CredentialModel credential = null) { try { var requestApiModel = new PublishStartRequestApiModel() { Item = new PublishedItemApiModel() { NodeId = nodeId, DisplayName = displayName, - SamplingInterval = TimeSpan.FromMilliseconds(samplingInterval), - PublishingInterval = TimeSpan.FromMilliseconds(publishingInterval) + SamplingInterval = samplingInterval, + PublishingInterval = publishingInterval, + HeartbeatInterval = heartBeatInterval } }; From 5c82a0f78119a8ebd3e47595d1a8000d14d6690b Mon Sep 17 00:00:00 2001 From: Luis Cantero Date: Fri, 8 May 2020 10:41:39 +0200 Subject: [PATCH 2/3] Add final newline to .json and .sh files (#427) Co-authored-by: Marc Schier --- .editorconfig | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/.editorconfig b/.editorconfig index 9d25f1d6e9..cc0a71ea79 100644 --- a/.editorconfig +++ b/.editorconfig @@ -15,6 +15,13 @@ tab_width = 4 end_of_line = crlf insert_final_newline = false +# JSON files, shell scripts +[*.json, *.sh] + +# New line preferences +end_of_line = lf +insert_final_newline = true + #### .NET Coding Conventions #### # Organize usings From 25103a97d0a79fce642f35ac92caf7ea983477a6 Mon Sep 17 00:00:00 2001 From: Karapet Kostandyan Date: Fri, 8 May 2020 10:42:04 +0200 Subject: [PATCH 3/3] Fixing KeyVault configuration provider priority for onboarding and tunnel services. (#429) Co-authored-by: Marc Schier --- .../src/Program.cs | 4 +++- .../src/Program.cs | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/services/src/Microsoft.Azure.IIoT.Services.Processor.Onboarding/src/Program.cs b/services/src/Microsoft.Azure.IIoT.Services.Processor.Onboarding/src/Program.cs index f40dac826a..404b07957e 100644 --- a/services/src/Microsoft.Azure.IIoT.Services.Processor.Onboarding/src/Program.cs +++ b/services/src/Microsoft.Azure.IIoT.Services.Processor.Onboarding/src/Program.cs @@ -51,8 +51,10 @@ public static void Main(string[] args) { .AddEnvironmentVariables() .AddEnvironmentVariables(EnvironmentVariableTarget.User) .AddFromDotEnvFile() - .AddFromKeyVault() .AddCommandLine(args) + // Above configuration providers will provide connection + // details for KeyVault configuration provider. + .AddFromKeyVault(providerPriority: ConfigurationProviderPriority.Lowest) .Build(); // Set up dependency injection for the event processor host diff --git a/services/src/Microsoft.Azure.IIoT.Services.Processor.Tunnel/src/Program.cs b/services/src/Microsoft.Azure.IIoT.Services.Processor.Tunnel/src/Program.cs index 8968c57be8..763979db77 100644 --- a/services/src/Microsoft.Azure.IIoT.Services.Processor.Tunnel/src/Program.cs +++ b/services/src/Microsoft.Azure.IIoT.Services.Processor.Tunnel/src/Program.cs @@ -43,8 +43,10 @@ public static void Main(string[] args) { .AddEnvironmentVariables() .AddEnvironmentVariables(EnvironmentVariableTarget.User) .AddFromDotEnvFile() - .AddFromKeyVault() .AddCommandLine(args) + // Above configuration providers will provide connection + // details for KeyVault configuration provider. + .AddFromKeyVault(providerPriority: ConfigurationProviderPriority.Lowest) .Build(); // Set up dependency injection for the event processor host