Merge pull request #15 from marcschier/master
opc-ua module v2 for .net standard loader.
This commit is contained in:
Коммит
507e0fe94c
|
@ -1,223 +1,16 @@
|
|||
# Compiled object files
|
||||
*.o
|
||||
*.opp
|
||||
|
||||
# Compiled static libraries
|
||||
*.a
|
||||
|
||||
## Ignore Visual Studio temporary files, build results, and
|
||||
## files generated by popular Visual Studio add-ons.
|
||||
|
||||
# User-specific files
|
||||
*.suo
|
||||
*.user
|
||||
*.sln.docstates
|
||||
|
||||
# Build results
|
||||
|
||||
build/
|
||||
[Dd]ebug/
|
||||
[Rr]elease/
|
||||
x64/
|
||||
[Bb]in/
|
||||
[Oo]bj/
|
||||
|
||||
# Enable "build/" folder in the NuGet Packages folder since NuGet packages use it for MSBuild targets
|
||||
!packages/*/build/
|
||||
|
||||
# MSTest test Results
|
||||
[Tt]est[Rr]esult*/
|
||||
[Bb]uild[Ll]og.*
|
||||
|
||||
*_i.c
|
||||
*_p.c
|
||||
*.ilk
|
||||
*.meta
|
||||
*.obj
|
||||
*.pch
|
||||
*.pdb
|
||||
*.pgc
|
||||
*.pgd
|
||||
*.rsp
|
||||
*.sbr
|
||||
*.tlb
|
||||
*.tli
|
||||
*.tlh
|
||||
*.tmp
|
||||
*.tmp_proj
|
||||
*.log
|
||||
*.vspscc
|
||||
*.vssscc
|
||||
.builds
|
||||
*.pidb
|
||||
*.log
|
||||
*.scc
|
||||
|
||||
# Visual C++ cache files
|
||||
ipch/
|
||||
*.aps
|
||||
*.ncb
|
||||
*.opensdf
|
||||
*.sdf
|
||||
*.cachefile
|
||||
|
||||
# Visual Studio profiler
|
||||
*.psess
|
||||
*.vsp
|
||||
*.vspx
|
||||
|
||||
# Guidance Automation Toolkit
|
||||
*.gpState
|
||||
|
||||
# ReSharper is a .NET coding add-in
|
||||
_ReSharper*/
|
||||
*.[Rr]e[Ss]harper
|
||||
|
||||
# TeamCity is a build add-in
|
||||
_TeamCity*
|
||||
|
||||
# DotCover is a Code Coverage Tool
|
||||
*.dotCover
|
||||
|
||||
# NCrunch
|
||||
*.ncrunch*
|
||||
.*crunch*.local.xml
|
||||
|
||||
# Installshield output folder
|
||||
[Ee]xpress/
|
||||
|
||||
# DocProject is a documentation generator add-in
|
||||
DocProject/buildhelp/
|
||||
DocProject/Help/*.HxT
|
||||
DocProject/Help/*.HxC
|
||||
DocProject/Help/*.hhc
|
||||
DocProject/Help/*.hhk
|
||||
DocProject/Help/*.hhp
|
||||
DocProject/Help/Html2
|
||||
DocProject/Help/html
|
||||
|
||||
# Click-Once directory
|
||||
publish/
|
||||
|
||||
# Publish Web Output
|
||||
*.Publish.xml
|
||||
|
||||
# NuGet Packages Directory
|
||||
packages/
|
||||
|
||||
# Windows Azure Build Output
|
||||
csx
|
||||
*.build.csdef
|
||||
|
||||
# Windows Store app package directory
|
||||
AppPackages/
|
||||
|
||||
# Others
|
||||
sql/
|
||||
*.Cache
|
||||
ClientBin/
|
||||
[Ss]tyle[Cc]op.*
|
||||
~$*
|
||||
*~
|
||||
*.dbmdl
|
||||
*.[Pp]ublish.xml
|
||||
*.publishsettings
|
||||
|
||||
# RIA/Silverlight projects
|
||||
Generated_Code/
|
||||
|
||||
# Backup & report files from converting an old project file to a newer
|
||||
# Visual Studio version. Backup files are not needed, because we have git ;-)
|
||||
_UpgradeReport_Files/
|
||||
Backup*/
|
||||
UpgradeLog*.XML
|
||||
UpgradeLog*.htm
|
||||
|
||||
# SQL Server files
|
||||
App_Data/*.mdf
|
||||
App_Data/*.ldf
|
||||
|
||||
|
||||
#LightSwitch generated files
|
||||
GeneratedArtifacts/
|
||||
_Pvt_Extensions/
|
||||
ModelManifest.xml
|
||||
|
||||
# =========================
|
||||
# Windows detritus
|
||||
# =========================
|
||||
|
||||
# Windows image file caches
|
||||
Thumbs.db
|
||||
ehthumbs.db
|
||||
|
||||
# Folder config file
|
||||
Desktop.ini
|
||||
|
||||
# Recycle Bin used on file shares
|
||||
$RECYCLE.BIN/
|
||||
|
||||
# Mac desktop service store files
|
||||
.DS_Store
|
||||
|
||||
# Visual studio build artifacts
|
||||
*.tlog
|
||||
*.lastbuildstate
|
||||
*.idb
|
||||
*.exp
|
||||
*.lib
|
||||
*.dll
|
||||
|
||||
# Windows CE build artifacts
|
||||
Build.err
|
||||
Build.wrn
|
||||
Buildx86retail.dat
|
||||
*.dat
|
||||
|
||||
# Tools EXE that doesn't end up in a typical build directory
|
||||
c/common/tools/macro_utils_h_generator/macro_utils_h_generator.exe
|
||||
|
||||
# hg directories should be ignored
|
||||
**/hg/
|
||||
|
||||
# Java
|
||||
**/java/**/.idea/
|
||||
**/java/**/out/
|
||||
**/java/**/target/
|
||||
**/java/**/*.iml
|
||||
**/java/**/*dependency-reduced-pom.xml
|
||||
tools/jenkins-cli.jar
|
||||
*.class
|
||||
*.jar
|
||||
**/target/
|
||||
*.iml
|
||||
|
||||
# Node.js
|
||||
**/.settings/
|
||||
**/node_modules/
|
||||
**/.idea/
|
||||
|
||||
# VS Code stuff
|
||||
typings/**
|
||||
.vscode/**
|
||||
|
||||
# ignore cmake build folder
|
||||
.cmake/**
|
||||
*build/**
|
||||
build_nodejs/**
|
||||
|
||||
# ignore Atom Editor files
|
||||
.atom-build.json
|
||||
.remote-sync.json
|
||||
|
||||
# ignore VS Code C++ files
|
||||
browse.VC.db
|
||||
|
||||
# api reference docstates
|
||||
api_reference/
|
||||
|
||||
/Opc.Ua.Client.Module.VC.VC.opendb
|
||||
/Opc.Ua.Client.Module.VC.db
|
||||
/binding/OPC Foundation/CertificateStores
|
||||
/.vs/config
|
||||
/OPC Foundation/CertificateStores/MachineDefault
|
||||
/**/*.user
|
||||
/**/*.user.json
|
||||
/**/project.lock.json
|
||||
/**/obj/**
|
||||
/**/.vs
|
||||
/**/bin/**
|
||||
/**/csx/**
|
||||
/**/ecf/**
|
||||
/**/*.log
|
||||
/**/*.err
|
||||
/**/*.sdf
|
||||
/**/*.opendb
|
||||
/**/*.opensdf
|
||||
/**/*.vc.db
|
||||
/.vscode/**
|
||||
/build/**
|
||||
|
|
Двоичный файл не отображается.
Двоичный файл не отображается.
324
Module.cs
324
Module.cs
|
@ -1,324 +0,0 @@
|
|||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using System.Runtime.Serialization;
|
||||
using System.Text;
|
||||
using Microsoft.Azure.IoT.Gateway;
|
||||
using Opc.Ua.Configuration;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Opc.Ua.Client
|
||||
{
|
||||
[DataContract(Name = "NodeLookup", Namespace = Namespaces.OpcUaXsd)]
|
||||
public partial class NodeLookup
|
||||
{
|
||||
public NodeLookup()
|
||||
{
|
||||
}
|
||||
|
||||
[DataMember(Name = "EndpointUrl", IsRequired = true, Order = 0)]
|
||||
public Uri EndPointURL;
|
||||
|
||||
[DataMember(Name = "NodeId", IsRequired = true, Order = 1)]
|
||||
public NodeId NodeID;
|
||||
}
|
||||
|
||||
[CollectionDataContract(Name = "ListOfPublishedNodes", Namespace = Namespaces.OpcUaConfig, ItemName = "NodeLookup")]
|
||||
public partial class PublishedNodesCollection : List<NodeLookup>
|
||||
{
|
||||
public PublishedNodesCollection()
|
||||
{
|
||||
}
|
||||
|
||||
public static PublishedNodesCollection Load(ApplicationConfiguration configuration)
|
||||
{
|
||||
return configuration.ParseExtension<PublishedNodesCollection>();
|
||||
}
|
||||
}
|
||||
|
||||
public class SampleModule : IGatewayModule, IGatewayModuleStart
|
||||
{
|
||||
private Broker m_broker;
|
||||
|
||||
private ApplicationConfiguration m_configuration = null;
|
||||
private List<Session> m_sessions = new List<Session>();
|
||||
|
||||
private string m_DeviceID = string.Empty;
|
||||
private string m_SharedAccessKey = string.Empty;
|
||||
|
||||
public async void Create(Broker broker, byte[] configuration)
|
||||
{
|
||||
m_broker = broker;
|
||||
|
||||
// TODO: Security: The shared access key should be stored in secure storage, e.g. a TPM
|
||||
// and the device ID can be used as a lookup
|
||||
string configurationString = JsonConvert.DeserializeObject<string>(Encoding.UTF8.GetString(configuration));
|
||||
m_DeviceID = configurationString.Substring(0, configurationString.IndexOf(';'));
|
||||
m_SharedAccessKey = configurationString.Substring(configurationString.IndexOf(';') + 1);
|
||||
|
||||
// load the application configuration.
|
||||
ApplicationInstance application = new ApplicationInstance();
|
||||
application.ConfigSectionName = "Opc.Ua.Client.SampleModule";
|
||||
m_configuration = await application.LoadApplicationConfiguration(false);
|
||||
|
||||
// check the application certificate.
|
||||
await application.CheckApplicationInstanceCertificate(false, 0);
|
||||
|
||||
m_configuration.CertificateValidator.CertificateValidation += new CertificateValidationEventHandler(CertificateValidator_CertificateValidation);
|
||||
|
||||
// get a list of persisted endpoint URLs and create a list without duplicates.
|
||||
List<Uri> endpointUrls = new List<Uri>();
|
||||
PublishedNodesCollection nodesLookups = PublishedNodesCollection.Load(m_configuration);
|
||||
foreach (NodeLookup nodeLookup in nodesLookups)
|
||||
{
|
||||
if (!endpointUrls.Contains(nodeLookup.EndPointURL))
|
||||
{
|
||||
endpointUrls.Add(nodeLookup.EndPointURL);
|
||||
}
|
||||
}
|
||||
|
||||
// now create a session for each unique endpoint
|
||||
foreach (Uri endpointUrl in endpointUrls)
|
||||
{
|
||||
try
|
||||
{
|
||||
Console.WriteLine("Opc.Ua.Client.SampleModule: Creating session for endpoint: " + endpointUrl.ToString());
|
||||
await EndpointConnect(endpointUrl);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
string innerException = ex.InnerException != null ? "\r\n. Inner Exception: " + ex.InnerException.ToString() : String.Empty;
|
||||
Console.WriteLine("Opc.Ua.Client.SampleModule: Could not connect to updated endpoint " + endpointUrl.ToString() + ". Exception: " + ex.ToString() + innerException);
|
||||
}
|
||||
}
|
||||
|
||||
Console.WriteLine("Opc.Ua.Client.SampleModule: OPC UA Client Sample Module created.");
|
||||
}
|
||||
|
||||
private async Task EndpointConnect(Uri endpointUrl)
|
||||
{
|
||||
EndpointDescription selectedEndpoint = SelectUaTcpEndpoint(DiscoverEndpoints(m_configuration, endpointUrl, 60));
|
||||
ConfiguredEndpoint configuredEndpoint = new ConfiguredEndpoint(selectedEndpoint.Server, EndpointConfiguration.Create(m_configuration));
|
||||
configuredEndpoint.Update(selectedEndpoint);
|
||||
|
||||
Session newSession = await Session.Create(
|
||||
m_configuration,
|
||||
configuredEndpoint,
|
||||
true,
|
||||
false,
|
||||
m_configuration.ApplicationName,
|
||||
60000,
|
||||
new UserIdentity(new AnonymousIdentityToken()),
|
||||
null);
|
||||
|
||||
if (newSession != null)
|
||||
{
|
||||
Console.WriteLine("Opc.Ua.Client.SampleModule: Created session with updated endpoint " + configuredEndpoint.EndpointUrl + " from server!");
|
||||
newSession.KeepAlive += new KeepAliveEventHandler(StandardClient_KeepAlive);
|
||||
m_sessions.Add(newSession);
|
||||
}
|
||||
}
|
||||
|
||||
public void Start()
|
||||
{
|
||||
// publish preconfigured nodes
|
||||
PublishedNodesCollection nodesLookups = PublishedNodesCollection.Load(m_configuration);
|
||||
foreach (NodeLookup nodeLookup in nodesLookups)
|
||||
{
|
||||
CreateMonitoredItem(nodeLookup);
|
||||
}
|
||||
|
||||
Console.WriteLine("Opc.Ua.Client.SampleModule: OPC UA Client Sample Module started.");
|
||||
}
|
||||
|
||||
public void Destroy()
|
||||
{
|
||||
foreach (Session session in m_sessions)
|
||||
{
|
||||
// Disconnect and dispose
|
||||
session.Dispose();
|
||||
}
|
||||
|
||||
m_sessions.Clear();
|
||||
|
||||
Console.WriteLine("Opc.Ua.Client.SampleModule: OPC UA Client Sample Module destroyed.");
|
||||
}
|
||||
|
||||
public void Receive(Message received_message)
|
||||
{
|
||||
// Nothing to do, we only send!
|
||||
}
|
||||
|
||||
public void CreateMonitoredItem(NodeLookup nodeLookup)
|
||||
{
|
||||
// find the right session using our lookup
|
||||
Session matchingSession = null;
|
||||
foreach(Session session in m_sessions)
|
||||
{
|
||||
if (session.Endpoint.EndpointUrl.ToLowerInvariant().TrimEnd('/') == Utils.ReplaceLocalhost(nodeLookup.EndPointURL.ToString()).ToLowerInvariant().TrimEnd('/'))
|
||||
{
|
||||
matchingSession = session;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (matchingSession != null)
|
||||
{
|
||||
Subscription subscription = matchingSession.DefaultSubscription;
|
||||
if (matchingSession.AddSubscription(subscription))
|
||||
{
|
||||
subscription.Create();
|
||||
}
|
||||
|
||||
// add the new monitored item.
|
||||
MonitoredItem monitoredItem = new MonitoredItem(subscription.DefaultItem);
|
||||
|
||||
monitoredItem.StartNodeId = nodeLookup.NodeID;
|
||||
monitoredItem.AttributeId = Attributes.Value;
|
||||
monitoredItem.DisplayName = nodeLookup.NodeID.Identifier.ToString();
|
||||
monitoredItem.MonitoringMode = MonitoringMode.Reporting;
|
||||
monitoredItem.SamplingInterval = 0;
|
||||
monitoredItem.QueueSize = 0;
|
||||
monitoredItem.DiscardOldest = true;
|
||||
|
||||
monitoredItem.Notification += new MonitoredItemNotificationEventHandler(MonitoredItem_Notification);
|
||||
subscription.AddItem(monitoredItem);
|
||||
subscription.ApplyChanges();
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLine("Opc.Ua.Client.SampleModule: ERROR: Could not find endpoint URL " + nodeLookup.EndPointURL.ToString() + " in active server sessions, NodeID " + nodeLookup.NodeID.Identifier.ToString() + " NOT published!");
|
||||
Console.WriteLine("Opc.Ua.Client.SampleModule: To fix this, please update your config.xml with the updated enpoint URL!");
|
||||
}
|
||||
}
|
||||
|
||||
private void MonitoredItem_Notification(MonitoredItem monitoredItem, MonitoredItemNotificationEventArgs e)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (e.NotificationValue == null || monitoredItem.Subscription.Session == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
JsonEncoder encoder = new JsonEncoder(monitoredItem.Subscription.Session.MessageContext, false);
|
||||
string hostname = monitoredItem.Subscription.Session.ConfiguredEndpoint.EndpointUrl.DnsSafeHost;
|
||||
if (hostname == "localhost")
|
||||
{
|
||||
hostname = Utils.GetHostName();
|
||||
}
|
||||
encoder.WriteString("HostName", hostname);
|
||||
encoder.WriteNodeId("MonitoredItem", monitoredItem.ResolvedNodeId);
|
||||
e.NotificationValue.Encode(encoder);
|
||||
|
||||
string json = encoder.Close();
|
||||
|
||||
var properties = new Dictionary<string, string>();
|
||||
properties.Add("source", "mapping");
|
||||
properties.Add("content-type", "application/opcua+uajson");
|
||||
properties.Add("deviceName", m_DeviceID);
|
||||
properties.Add("deviceKey", m_SharedAccessKey);
|
||||
|
||||
try
|
||||
{
|
||||
m_broker.Publish(new Message(json, properties));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Utils.Trace(ex, "Opc.Ua.Client.SampleModule: Failed to publish message, dropping....");
|
||||
}
|
||||
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
Utils.Trace(exception, "Opc.Ua.Client.SampleModule: Error processing monitored item notification.");
|
||||
}
|
||||
}
|
||||
|
||||
private void CertificateValidator_CertificateValidation(CertificateValidator validator, CertificateValidationEventArgs e)
|
||||
{
|
||||
if (e.Error.StatusCode == StatusCodes.BadCertificateUntrusted)
|
||||
{
|
||||
e.Accept = true;
|
||||
Console.WriteLine("Opc.Ua.Client.SampleModule: WARNING: Auto-accepting certificate: {0}", e.Certificate.Subject);
|
||||
}
|
||||
}
|
||||
|
||||
private void StandardClient_KeepAlive(Session sender, KeepAliveEventArgs e)
|
||||
{
|
||||
if (e != null && sender != null)
|
||||
{
|
||||
if (!ServiceResult.IsGood(e.Status))
|
||||
{
|
||||
Console.WriteLine(String.Format(
|
||||
"Opc.Ua.Client.SampleModule: Server {0} Status NOT good: {1} {2}/{3}",
|
||||
sender.ConfiguredEndpoint.EndpointUrl,
|
||||
e.Status,
|
||||
sender.OutstandingRequestCount,
|
||||
sender.DefunctRequestCount));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private EndpointDescriptionCollection DiscoverEndpoints(ApplicationConfiguration config, Uri discoveryUrl, int timeout)
|
||||
{
|
||||
EndpointConfiguration configuration = EndpointConfiguration.Create(config);
|
||||
configuration.OperationTimeout = timeout;
|
||||
|
||||
using (DiscoveryClient client = DiscoveryClient.Create(
|
||||
discoveryUrl,
|
||||
EndpointConfiguration.Create(config)))
|
||||
{
|
||||
try
|
||||
{
|
||||
EndpointDescriptionCollection endpoints = client.GetEndpoints(null);
|
||||
ReplaceLocalHostWithRemoteHost(endpoints, discoveryUrl);
|
||||
return endpoints;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Console.WriteLine("Opc.Ua.Client.SampleModule: Could not fetch endpoints from url: {0}", discoveryUrl);
|
||||
Console.WriteLine("Opc.Ua.Client.SampleModule: Reason = {0}", e.Message);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void ReplaceLocalHostWithRemoteHost(EndpointDescriptionCollection endpoints, Uri discoveryUrl)
|
||||
{
|
||||
foreach (EndpointDescription endpoint in endpoints)
|
||||
{
|
||||
endpoint.EndpointUrl = Utils.ReplaceLocalhost(endpoint.EndpointUrl, discoveryUrl.DnsSafeHost);
|
||||
StringCollection updatedDiscoveryUrls = new StringCollection();
|
||||
|
||||
foreach (string url in endpoint.Server.DiscoveryUrls)
|
||||
{
|
||||
updatedDiscoveryUrls.Add(Utils.ReplaceLocalhost(url, discoveryUrl.DnsSafeHost));
|
||||
}
|
||||
|
||||
endpoint.Server.DiscoveryUrls = updatedDiscoveryUrls;
|
||||
}
|
||||
}
|
||||
|
||||
private EndpointDescription SelectUaTcpEndpoint(EndpointDescriptionCollection endpointCollection)
|
||||
{
|
||||
EndpointDescription bestEndpoint = null;
|
||||
foreach (EndpointDescription endpoint in endpointCollection)
|
||||
{
|
||||
if (endpoint.TransportProfileUri == Profiles.UaTcpTransport)
|
||||
{
|
||||
if ((bestEndpoint == null) ||
|
||||
(endpoint.SecurityLevel > bestEndpoint.SecurityLevel))
|
||||
{
|
||||
bestEndpoint = endpoint;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return bestEndpoint;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<configuration>
|
||||
<solution>
|
||||
<add key="disableSourceControlIntegration" value="true" />
|
||||
</solution>
|
||||
<config>
|
||||
<add key="repositorypath" value="..\packages" />
|
||||
</config>
|
||||
<packageSources>
|
||||
<add key="nugetv2" value="https://www.nuget.org/api/v2/" />
|
||||
<add key="nugetv2dotnet" value="https://www.nuget.org/api/v2/curated-feeds/microsoftdotnet/" />
|
||||
<add key="nugetv3" value="https://api.nuget.org/v3/index.json" />
|
||||
<!-- To enable LocalFeed for testing uncomment the following line -->
|
||||
<add key="Local" value=".nuget" />
|
||||
</packageSources>
|
||||
</configuration>
|
|
@ -1,112 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||
<ProjectGuid>{25D19FFD-D553-4270-8A7A-6F510572BDC0}</ProjectGuid>
|
||||
<OutputType>Library</OutputType>
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<RootNamespace>Opc.Ua.Client</RootNamespace>
|
||||
<AssemblyName>Opc.Ua.Client.SampleModule</AssemblyName>
|
||||
<TargetFrameworkVersion>v4.6</TargetFrameworkVersion>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
<TargetFrameworkProfile />
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>full</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<OutputPath>bin\Debug\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<Optimize>true</Optimize>
|
||||
<OutputPath>bin\Release\</OutputPath>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x86'">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<OutputPath>bin\x86\Debug\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<DebugType>full</DebugType>
|
||||
<PlatformTarget>x86</PlatformTarget>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x86'">
|
||||
<OutputPath>bin\x86\Release\</OutputPath>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<Optimize>true</Optimize>
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<PlatformTarget>x86</PlatformTarget>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="BouncyCastle.Crypto, Version=1.8.1.0, Culture=neutral, PublicKeyToken=0e99375e54769942, processorArchitecture=MSIL">
|
||||
<HintPath>packages\Portable.BouncyCastle.1.8.1.2\lib\net4\BouncyCastle.Crypto.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.Azure.IoT.Gateway, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<HintPath>packages\Azure.IoT.Gateway.SDK.Net.2017.1.13.2\lib\net40\Microsoft.Azure.IoT.Gateway.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="Newtonsoft.Json, Version=9.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
|
||||
<HintPath>packages\Newtonsoft.Json.9.0.1\lib\net45\Newtonsoft.Json.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="Opc.Ua.Client, Version=1.3.340.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<HintPath>packages\OPCFoundation.NetStandard.Opc.Ua.SDK.0.1.1\lib\net46\Opc.Ua.Client.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Opc.Ua.Configuration, Version=1.3.340.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<HintPath>packages\OPCFoundation.NetStandard.Opc.Ua.SDK.0.1.1\lib\net46\Opc.Ua.Configuration.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Opc.Ua.Core, Version=1.3.340.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<HintPath>packages\OPCFoundation.NetStandard.Opc.Ua.Core.0.1.1\lib\net46\Opc.Ua.Core.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Core" />
|
||||
<Reference Include="System.Runtime.Serialization" />
|
||||
<Reference Include="System.Xml.Linq" />
|
||||
<Reference Include="System.Data.DataSetExtensions" />
|
||||
<Reference Include="Microsoft.CSharp" />
|
||||
<Reference Include="System.Data" />
|
||||
<Reference Include="System.Net.Http" />
|
||||
<Reference Include="System.Xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="Module.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Content Include="gateway_config.json">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<None Include="packages.config">
|
||||
<SubType>Designer</SubType>
|
||||
</None>
|
||||
<None Include="README.md" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Content Include="Opc.Ua.Client.SampleModule.Config.xml">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</Content>
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||
Other similar extension points exist, see Microsoft.Common.targets.
|
||||
<Target Name="BeforeBuild">
|
||||
</Target>
|
||||
<Target Name="AfterBuild">
|
||||
</Target>
|
||||
-->
|
||||
</Project>
|
|
@ -3,27 +3,21 @@ Microsoft Visual Studio Solution File, Format Version 12.00
|
|||
# Visual Studio 14
|
||||
VisualStudioVersion = 14.0.25420.1
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Opc.Ua.Client.Module", "Opc.Ua.Client.Module.csproj", "{25D19FFD-D553-4270-8A7A-6F510572BDC0}"
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "dotnet_binding_sample", "binding\dotnet_binding_sample.vcxproj", "{7064A025-384F-3689-89FD-9E480DBFE5C9}"
|
||||
ProjectSection(ProjectDependencies) = postProject
|
||||
{25D19FFD-D553-4270-8A7A-6F510572BDC0} = {25D19FFD-D553-4270-8A7A-6F510572BDC0}
|
||||
EndProjectSection
|
||||
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Opc.Ua.Client.Module", "src\Opc.Ua.Client.Module\Opc.Ua.Client.Module.xproj", "{43311AFB-D7C4-4E5A-B1DE-855407F90D1B}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|x86 = Debug|x86
|
||||
Release|x86 = Release|x86
|
||||
Signed|x86 = Signed|x86
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{25D19FFD-D553-4270-8A7A-6F510572BDC0}.Debug|x86.ActiveCfg = Debug|x86
|
||||
{25D19FFD-D553-4270-8A7A-6F510572BDC0}.Debug|x86.Build.0 = Debug|x86
|
||||
{25D19FFD-D553-4270-8A7A-6F510572BDC0}.Release|x86.ActiveCfg = Release|x86
|
||||
{25D19FFD-D553-4270-8A7A-6F510572BDC0}.Release|x86.Build.0 = Release|x86
|
||||
{7064A025-384F-3689-89FD-9E480DBFE5C9}.Debug|x86.ActiveCfg = Debug|Win32
|
||||
{7064A025-384F-3689-89FD-9E480DBFE5C9}.Debug|x86.Build.0 = Debug|Win32
|
||||
{7064A025-384F-3689-89FD-9E480DBFE5C9}.Release|x86.ActiveCfg = Release|Win32
|
||||
{7064A025-384F-3689-89FD-9E480DBFE5C9}.Release|x86.Build.0 = Release|Win32
|
||||
{43311AFB-D7C4-4E5A-B1DE-855407F90D1B}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{43311AFB-D7C4-4E5A-B1DE-855407F90D1B}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{43311AFB-D7C4-4E5A-B1DE-855407F90D1B}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{43311AFB-D7C4-4E5A-B1DE-855407F90D1B}.Release|x86.Build.0 = Release|Any CPU
|
||||
{43311AFB-D7C4-4E5A-B1DE-855407F90D1B}.Signed|x86.ActiveCfg = Signed|Any CPU
|
||||
{43311AFB-D7C4-4E5A-B1DE-855407F90D1B}.Signed|x86.Build.0 = Signed|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
|
|
|
@ -1,249 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<ApplicationConfiguration
|
||||
xmlns:ua="http://opcfoundation.org/UA/2008/02/Types.xsd"
|
||||
xmlns="http://opcfoundation.org/UA/SDK/Configuration.xsd"
|
||||
schemaLocation="./Schema/ApplicationConfiguration.xsd"
|
||||
>
|
||||
<!-- A human readable but not necessarily unique name for the application instance -->
|
||||
<ApplicationName>Opc.Ua.Client.SampleModule</ApplicationName>
|
||||
|
||||
<!-- A globally unique identifier for the application instance.
|
||||
This is overridden with the value contained in the application certificate. -->
|
||||
<ApplicationUri>urn:localhost:OPCFoundation:SampleModule</ApplicationUri>
|
||||
|
||||
<!-- A globally unique URI for the product (usually assigned by the product vendor) -->
|
||||
<ProductUri>http://opcfoundation.org/UA/SampleModule/</ProductUri>
|
||||
|
||||
<!-- Indicates the type of application (Client, Server or ClientServer). -->
|
||||
<ApplicationType>Client_1</ApplicationType>
|
||||
|
||||
<!-- Specifies security related configuration information -->
|
||||
<SecurityConfiguration>
|
||||
|
||||
<!-- The location of the application instance certificate in the Windows certificate store -->
|
||||
<ApplicationCertificate>
|
||||
|
||||
<!-- The type of store. -->
|
||||
<StoreType>Directory</StoreType>
|
||||
|
||||
<!-- The location of the store.
|
||||
Windows store must start with LocalMachine, CurrentUser or CurrentService
|
||||
The name of the store is appended.
|
||||
Note that the names used in code are difference from what appears in the control panel.
|
||||
e.g. My == "Personal", Root == "Trusted Root Certification Authorities" -->
|
||||
<StorePath>OPC Foundation/CertificateStores/MachineDefault</StorePath>
|
||||
|
||||
|
||||
<!-- The subject for the certificate
|
||||
Note that subject names are complex structures. The text that appears here is the CommonName component.
|
||||
A complete distinguished would be something like: 'CN=Opc.Ua.Client.SampleModule, DC=MACHINENAME'
|
||||
The first certificate found is used if multiple certificates with the same CommonName exist.
|
||||
The Thumbprint should be specified if the CommonName does not uniquely identify a certificate. -->
|
||||
<SubjectName>Opc.Ua.Client.SampleModule</SubjectName>
|
||||
|
||||
<!-- The SHA1 thumbprint for the certificate.
|
||||
The thumbprint uniquely identifies a certificate.
|
||||
It should be specified in this file, however, the samples rely on quick and
|
||||
dirty scripts to create new certificate on each machine. A commerical application
|
||||
would generate the initial certificate itself and update the thumbprint accordingly -->
|
||||
<!--<Thumbprint>3a35fb798fc6dee8a7e7e4652b0e28fc14c6ee0f</Thumbprint>-->
|
||||
|
||||
</ApplicationCertificate>
|
||||
|
||||
<!-- The list of certification authorities.
|
||||
|
||||
Typical web browsing applications trust any certificate issued by a CA in the
|
||||
"Trusted Root Certification Authorities" certificate store. However, this approach is
|
||||
not appropriate for UA because Adminstrators have no control over the CAs that get
|
||||
placed in that Root store to facilitate web browsing. This means Adminstrators must
|
||||
specify a different store that is used only for UA related CAs and/or they must explicitly
|
||||
specify the certificate for each trusted certification authority. -->
|
||||
<TrustedIssuerCertificates>
|
||||
<StoreType>Directory</StoreType>
|
||||
<StorePath>OPC Foundation/CertificateStores/UA Certificate Authorities</StorePath>
|
||||
</TrustedIssuerCertificates>
|
||||
|
||||
<!-- The list of trusted certificates.
|
||||
|
||||
Some UA applications will use self-signed certificates (certificates without a CA)
|
||||
which means that every application which communicates with it must be configured to
|
||||
trust it.
|
||||
|
||||
Adminstrators may designate a certificate store that contains trusted UA application
|
||||
instance certificates (this store should not be the same as the store used for CAs
|
||||
certificates). Alternately, Administrators may enter the certificates explicitly in
|
||||
this list.
|
||||
|
||||
Note that entries in this list may either reference a certificate in the store or
|
||||
may contained the entire certificate encoded as base64 data.
|
||||
-->
|
||||
<TrustedPeerCertificates>
|
||||
<StoreType>Directory</StoreType>
|
||||
<StorePath>OPC Foundation/CertificateStores/UA Applications</StorePath>
|
||||
</TrustedPeerCertificates>
|
||||
|
||||
<!-- Applications exchange Nonces during the CreateSession. This value specifies the length. Must be >= 32 -->
|
||||
<NonceLength>32</NonceLength>
|
||||
|
||||
<!-- The directory used to store invalid certficates for later review by the administrator. -->
|
||||
<RejectedCertificateStore>
|
||||
<StoreType>Directory</StoreType>
|
||||
<StorePath>OPC Foundation/CertificateStores/RejectedCertificates</StorePath>
|
||||
</RejectedCertificateStore>
|
||||
|
||||
<!-- WARNING: The following setting (to automatically accept untrusted certificates) should be used
|
||||
for easy debugging purposes ONLY and turned off for production deployments! -->
|
||||
<AutoAcceptUntrustedCertificates>true</AutoAcceptUntrustedCertificates>
|
||||
|
||||
</SecurityConfiguration>
|
||||
|
||||
<TransportConfigurations></TransportConfigurations>
|
||||
|
||||
<!-- Specifies quotas used to by the transport layer -->
|
||||
<TransportQuotas>
|
||||
|
||||
<!-- The default timeout in milliseconds for operations (used by clients) -->
|
||||
<OperationTimeout>120000</OperationTimeout>
|
||||
|
||||
<!-- The maximum length for a string value in any message -->
|
||||
<MaxStringLength>1048576</MaxStringLength>
|
||||
|
||||
<!-- The maximum length for a byte string value in any message -->
|
||||
<MaxByteStringLength>4194304</MaxByteStringLength>
|
||||
|
||||
<!-- The maximum length for any array in a message.
|
||||
Note that some protocols do not distinguish between bytes and arrays.
|
||||
In these cases the binding will choose the larger of
|
||||
MaxByteStringLength or MaxArrayLength-->
|
||||
<MaxArrayLength>65535</MaxArrayLength>
|
||||
|
||||
<!-- The maximum size of any message -->
|
||||
<MaxMessageSize>4194304</MaxMessageSize>
|
||||
|
||||
<!-- The maximum buffer size
|
||||
This value controls how big a block of memory the transport layer allocates.
|
||||
Setting this value to a large value will reduce performance and use a lot of RAM -->
|
||||
<MaxBufferSize>65535</MaxBufferSize>
|
||||
|
||||
<!-- The lifetime of a SecureChannel in milliseconds.
|
||||
This specifies how long the server will keep a broken channel around while waiting
|
||||
for a client to reconnect.
|
||||
Not used by HTTP or .NET TCP bindings -->
|
||||
<ChannelLifetime>300000</ChannelLifetime>
|
||||
|
||||
<!-- The lifetime of a SecurityToken in milliseconds.
|
||||
This specifies how long a security token can be used without renewal. -->
|
||||
<SecurityTokenLifetime>3600000</SecurityTokenLifetime>
|
||||
|
||||
</TransportQuotas>
|
||||
|
||||
<!-- This element only needs to be specified for Server or ClientServer applications -->
|
||||
<ServerConfiguration/>
|
||||
|
||||
<!-- This element is only required for Client and ClientServer applications -->
|
||||
<ClientConfiguration>
|
||||
<!-- The default timeout for new sessions -->
|
||||
<DefaultSessionTimeout>600000</DefaultSessionTimeout>
|
||||
|
||||
<!-- The well-known URLs for the local discovery servers
|
||||
URLs are tested in the order they appear in this list. -->
|
||||
<WellKnownDiscoveryUrls>
|
||||
<ua:String>opc.tcp://{0}:4840/UADiscovery</ua:String>
|
||||
<ua:String>http://{0}:52601/UADiscovery</ua:String>
|
||||
<ua:String>http://{0}/UADiscovery/Default.svc</ua:String>
|
||||
</WellKnownDiscoveryUrls>
|
||||
|
||||
<!-- EndpointDescriptions for system wide discovery servers -->
|
||||
<DiscoveryServers></DiscoveryServers>
|
||||
|
||||
<!-- The file used to save the EndpointDescriptions for servers known to the Client -->
|
||||
<EndpointCacheFilePath>Opc.Ua.Client.SampleModule.Endpoints.xml</EndpointCacheFilePath>
|
||||
|
||||
<!-- The minimum subscription lifetime.
|
||||
This ensures subscriptions are not set to expire too quickly. The requesed lifetime count
|
||||
and keep alive count are calculated using this value and the request publishing interval -->
|
||||
<MinSubscriptionLifetime>10000</MinSubscriptionLifetime>
|
||||
|
||||
</ClientConfiguration>
|
||||
|
||||
<Extensions>
|
||||
<ua:XmlElement>
|
||||
<ListOfPublishedNodes xmlns="http://opcfoundation.org/UA/SDK/Configuration.xsd">
|
||||
<NodeLookup>
|
||||
<ua:EndpointUrl>opc.tcp://TODO:Add uri here</ua:EndpointUrl>
|
||||
<ua:NodeId>
|
||||
<!-- Current Server Time -->
|
||||
<ua:Identifier>i=2258</ua:Identifier>
|
||||
</ua:NodeId>
|
||||
</NodeLookup>
|
||||
<NodeLookup>
|
||||
<ua:EndpointUrl>opc.tcp://TODO:Add another uri here</ua:EndpointUrl>
|
||||
<ua:NodeId>
|
||||
<!-- Current Server Time -->
|
||||
<ua:Identifier>i=2258</ua:Identifier>
|
||||
</ua:NodeId>
|
||||
</NodeLookup>
|
||||
</ListOfPublishedNodes>
|
||||
</ua:XmlElement>
|
||||
</Extensions>
|
||||
|
||||
<!--
|
||||
Masks supported by the trace feature.
|
||||
Servers will detect changes within 5 seconds.
|
||||
|
||||
Do not output any messages.
|
||||
None = 0x0;
|
||||
|
||||
Output error messages.
|
||||
Error = 0x1;
|
||||
|
||||
Output informational messages.
|
||||
Information = 0x2;
|
||||
|
||||
Output stack traces.
|
||||
StackTrace = 0x4;
|
||||
|
||||
Output basic messages for service calls.
|
||||
Service = 0x8;
|
||||
|
||||
Output detailed messages for service calls.
|
||||
ServiceDetail = 0x10;
|
||||
|
||||
Output basic messages for each operation.
|
||||
Operation = 0x20;
|
||||
|
||||
Output detailed messages for each operation.
|
||||
OperationDetail = 0x40;
|
||||
|
||||
Output messages related to application initialization or shutdown
|
||||
StartStop = 0x80;
|
||||
|
||||
Output messages related to a call to an external system.
|
||||
ExternalSystem = 0x100;
|
||||
|
||||
Output messages related to security
|
||||
Security = 0x200;
|
||||
-->
|
||||
|
||||
<TraceConfiguration>
|
||||
<OutputFilePath>Logs/Opc.Ua.Client.SampleModule.log.txt</OutputFilePath>
|
||||
<DeleteOnLoad>true</DeleteOnLoad>
|
||||
<!-- Show Only Errors -->
|
||||
<!-- <TraceMasks>1</TraceMasks> -->
|
||||
<!-- Show Only Security and Errors -->
|
||||
<!-- <TraceMasks>513</TraceMasks> -->
|
||||
<!-- Show Only Security, Errors and Trace -->
|
||||
<TraceMasks>515</TraceMasks>
|
||||
<!-- Show Only Security, COM Calls, Errors and Trace -->
|
||||
<!-- <TraceMasks>771</TraceMasks> -->
|
||||
<!-- Show Only Security, Service Calls, Errors and Trace -->
|
||||
<!-- <TraceMasks>523</TraceMasks> -->
|
||||
<!-- Show Only Security, ServiceResultExceptions, Errors and Trace -->
|
||||
<!-- <TraceMasks>519</TraceMasks> -->
|
||||
</TraceConfiguration>
|
||||
|
||||
<!-- Enables the hi-res clock for the process to allows for shorter (<100ms) publishing and sampling intervals. -->
|
||||
<!-- QueryPerformanceCounter does not work on all multi-core machines so enabling the hi-res clock by default is not recommended. -->
|
||||
<DisableHiResClock>true</DisableHiResClock>
|
||||
|
||||
</ApplicationConfiguration>
|
96
README.md
96
README.md
|
@ -1,30 +1,92 @@
|
|||
This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments
|
||||
|
||||
# OPC UA Client Module for the Azure IoT Gateway SDK
|
||||
This reference implementation demonstrates how the Azure IoT Gateway SDK can be used to connect to existing OPC UA servers and send JSON encoded telemetry data from these servers in OPC UA "Pub/Sub" format (using a JSON payload) to Azure IoT Hub. All transport protocols supported by the Gateway SDK can be used, i.e. HTTPS, AMQP and MQTT. The transport is selected in the transport setting in gateway_config.JSON.
|
||||
This reference implementation demonstrates how the Azure IoT Gateway SDK can be used to connect to existing OPC UA servers and send JSON encoded telemetry data from these servers in OPC UA "Pub/Sub" format (using a JSON payload) to Azure IoT Hub. All transport protocols supported by the Gateway SDK can be used, i.e. HTTPS, AMQP and MQTT. The transport is selected in the transport setting in gateway_config.json.
|
||||
|
||||
This module uses the OPC Foundations's OPC UA reference stack and therefore licensing restrictions apply. Visit http://opcfoundation.github.io/UA-.NETStandardLibrary/ for OPC UA documentation and licensing terms.
|
||||
|
||||
## Operating System Compatibility
|
||||
Since this reference implementation is written for .NET, Windows 7, 8, 8.1 & 10 are supported right now. Once the Gateway SDK supports .NET Standard, so will this module and then Linux will also be supported.
|
||||
# Azure IoT Gateway SDK compatibility
|
||||
The current version of the Proxy module is targeted at the Azure IoT Gateway SDK at commit 09bbcb7feaf5acc3913abd722b96e993238edd0c.
|
||||
|
||||
## Directory Structure
|
||||
Use the following command line to clone the compatible version Azure IoT Gateway SDK, then follow the build instructions included:
|
||||
|
||||
### /binding
|
||||
This folder contains the native entry point required for the Gateway SDK (main.c).
|
||||
```
|
||||
git clone --recursive https://github.com/Azure/azure-iot-gateway-sdk.git
|
||||
git checkout 09bbcb7feaf5acc3913abd722b96e993238edd0c
|
||||
```
|
||||
|
||||
### Root Directory
|
||||
This folder contains the C# OPC UA module source file (Module.cs), the gateway configuration file (gateway_config.json) and the OPC UA gateway module configuration file (Opc.Ua.Client.SampleModule.Config.xml).
|
||||
The gateway needs to be build with the ```--enable_dotnetcore_binding``` flag to enable it to run this module.
|
||||
# Directory Structure
|
||||
|
||||
## Configuring the Module
|
||||
The OPC UA server endpoints the module should connect to and the list of OPC UA nodes for each endpoint that should be published to Azure IoT Hub can be configured in the **ListOfPublishedNodes** section of the **Opc.Ua.Client.SampleModule.Config.xml** configuration file. The **Current Server Time** node (node ID 2258) is specified as an example.
|
||||
## /samples
|
||||
This folder contains a sample configuration that instructs a vanilla gateway host load the module and IoT Hub proxy module and configures the module to create a
|
||||
subscription on a standard server which publishes the current server time to Azure IoT Hub.
|
||||
|
||||
Also, in the gateway_config.json, configure the name of IoT Hub you want to send the telemetry to (JSON field "IoTHubName") and the IoT Hub device ID and shared access key to use (JSON field "args").
|
||||
## /src
|
||||
This folder contains the C# OPC UA module source file (Module.cs).
|
||||
|
||||
## Building and Running the Module
|
||||
Simply open the solution file in Visual Studio and build it. Make sure you specify the gateway_config.json as a command line parameter, set the debugger type to Mixed and set the working directory to $(OutDir) before debugging.
|
||||
## /bld
|
||||
This folder contains build scripts for Windows and Linux.
|
||||
|
||||
# Building the Module
|
||||
|
||||
Run ```bld/build``` to build the module both Debug and Release. The published module can be found in ```build/release``` folder. Run the build script with the ```--help``` command line argument to see all build options.
|
||||
|
||||
# Configuring the Module
|
||||
OPC UA nodes whose values should be published to Azure IoT Hub can be configured in the module JSON configuration. A sample template configuration file can be found in ```samples/gateway_config.json```. The configuration consists of a OPC-UA Application Configuration and Subscriptions section.
|
||||
|
||||
## Application Configuration section
|
||||
The ```Configuration``` Section must contain at a minimum all items shown in the sample template. The JSON type conforms to the OPC UA reference stack serialization of the ```ApplicationConfiguration``` type.
|
||||
|
||||
E.g. to enable automatic certificate accept (discouraged), set ```"AutoAcceptUntrustedCertificates"``` to true (default is false) inside the ```SecurityConfiguration``` section:
|
||||
|
||||
``` JSON
|
||||
"args": {
|
||||
"Configuration": {
|
||||
"ApplicationName": "Opc.Ua.Client.SampleModule",
|
||||
"ApplicationType": "Client",
|
||||
"ApplicationUri": "urn:localhost:OPCFoundation:SampleModule",
|
||||
"SecurityConfiguration": {
|
||||
"ApplicationCertificate": {},
|
||||
"AutoAcceptUntrustedCertificates": true
|
||||
},
|
||||
```
|
||||
|
||||
## Subscriptions section
|
||||
The ```Subscriptions``` Section contains an array of OPC-UA sessions that the module should establish at startup, with each specifying a list of items to monitor. The ```MonitoredItems``` array type in the JSON configuration conforms to the OPC UA reference stack serialization specification of the same type.
|
||||
|
||||
E.g. the sample template shows how to monitor the **Current Server Time** node (node ID 2258):
|
||||
|
||||
``` JSON
|
||||
},
|
||||
"Subscriptions": [
|
||||
{
|
||||
"Id": "<DeviceID>",
|
||||
"SharedAccessKey": "<SharedAccessKey>",
|
||||
"ServerUrl": "opc.tcp://<hostname>:51210/UA/SampleServer",
|
||||
"MinimumSecurityLevel": 0,
|
||||
"MinimumSecurityMode": "SignAndEncrypt",
|
||||
"PublishingInterval": 400,
|
||||
"MonitoredItems": [
|
||||
{
|
||||
"StartNodeId": "i=2258",
|
||||
"NodeClass": 2,
|
||||
"DisplayName": "ServerStatusCurrentTime",
|
||||
"DiscardOldest": false
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
```
|
||||
|
||||
The JSON snippet above shows the default security settings, which can thus be ommitted. By default the session is created on the endpoint that supports ```SignAndEncrypt``` message mode, regardless of the security level advertised by the server (0). You can adjust these settings to customize the endpoint selection process, e.g. setting the Security mode to ```"Sign"``` will select all ```Sign``` and ```SignAndEncrypt``` endpoints, ```"None"``` will select all endpoints.
|
||||
|
||||
# Running the module
|
||||
|
||||
To run the module and have it publish to IoT Hub, configure the name of your Hub (JSON field ```"IoTHubName"```) and the IoT Hub device ID and shared access key to use (JSON fields ```"Id"``` and ```"SharedAccessKey"```) in your version of ```gateway_config.json```. Note that if you use a "Mapping" module in your configuration, you can omit the ```"SharedAccessKey"``` field. Finally, ensure that the right native module is configured, based on your platform (i.e. iothub.dll for Windows, libiothub.so for Linux, etc.).
|
||||
|
||||
You can build a sample gateway host as part of the Azure IoT Gateway SDK build system. Ensure that you pass the ```--enable_dotnetcore_binding``` to the build script and use one of the resulting sample gateway hosts.
|
||||
|
||||
To simplify this step, and to build a sample gateway to host the OPC-UA module - along with the module itself - clone the gateway SDK repo to your device and run the build script with the ```-i <root-of-sdk>```. The resulting release folder will contain not just the module and managed dependencies, but also a native IoT Hub proxy module as well as a ```sample_gateway``` executable that you can pass the updated JSON configuration to.
|
||||
|
||||
## Updating NuGet Packages
|
||||
There are manual steps required after updating the OPC UA or Gateway SDK NuGet packages:
|
||||
1. When updating the OPC UA NuGet packages, you need to re-add the new reference to the updated OPC UA DLLs in the Opc.Ua.Client.Module project as Visual Studio doesn't do that automatically for .Net Standard assemblies in .Net Framework projects.
|
||||
2. When updating the Gateway SDK NuGet package, you need to update the path in the post-build copy command for the Gateway SDK DLLs in the dotnet_binding_sample project.
|
||||
|
|
|
@ -1,175 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup Label="ProjectConfigurations">
|
||||
<ProjectConfiguration Include="Debug|Win32">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|Win32">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Globals">
|
||||
<ProjectGUID>{7064A025-384F-3689-89FD-9E480DBFE5C9}</ProjectGUID>
|
||||
<WindowsTargetPlatformVersion>8.1</WindowsTargetPlatformVersion>
|
||||
<Keyword>Win32Proj</Keyword>
|
||||
<Platform>Win32</Platform>
|
||||
<ProjectName>dotnet_binding_sample</ProjectName>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseOfMfc>false</UseOfMfc>
|
||||
<CharacterSet>MultiByte</CharacterSet>
|
||||
<PlatformToolset>v140</PlatformToolset>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseOfMfc>false</UseOfMfc>
|
||||
<CharacterSet>MultiByte</CharacterSet>
|
||||
<PlatformToolset>v140</PlatformToolset>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||
<ImportGroup Label="ExtensionSettings">
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<PropertyGroup Label="UserMacros" />
|
||||
<PropertyGroup>
|
||||
<_ProjectFileVersion>10.0.20506.1</_ProjectFileVersion>
|
||||
<OutDir Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">..\bin\x86\Debug\</OutDir>
|
||||
<IntDir Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">dotnet_binding_sample.dir\Debug\</IntDir>
|
||||
<TargetName Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">dotnet_binding_sample</TargetName>
|
||||
<TargetExt Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">.exe</TargetExt>
|
||||
<LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</LinkIncremental>
|
||||
<GenerateManifest Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</GenerateManifest>
|
||||
<OutDir Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">..\bin\x86\Release\</OutDir>
|
||||
<IntDir Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">dotnet_binding_sample.dir\Release\</IntDir>
|
||||
<TargetName Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">dotnet_binding_sample</TargetName>
|
||||
<TargetExt Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">.exe</TargetExt>
|
||||
<LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">false</LinkIncremental>
|
||||
<GenerateManifest Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</GenerateManifest>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<CustomBuildAfterTargets>
|
||||
</CustomBuildAfterTargets>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<CustomBuildAfterTargets>
|
||||
</CustomBuildAfterTargets>
|
||||
</PropertyGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<ClCompile>
|
||||
<AdditionalOptions> /guard:cf %(AdditionalOptions)</AdditionalOptions>
|
||||
<AdditionalIncludeDirectories>%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<AssemblerListingLocation>Debug/</AssemblerListingLocation>
|
||||
<BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
|
||||
<CompileAs>CompileAsC</CompileAs>
|
||||
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
|
||||
<ExceptionHandling>
|
||||
</ExceptionHandling>
|
||||
<InlineFunctionExpansion>Disabled</InlineFunctionExpansion>
|
||||
<Optimization>Disabled</Optimization>
|
||||
<PrecompiledHeader>NotUsing</PrecompiledHeader>
|
||||
<RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<PreprocessorDefinitions>WIN32;_WINDOWS;_DEBUG;ARCHITECTURE_x86=1;_CRT_SECURE_NO_WARNINGS;CMAKE_INTDIR="Debug";%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ObjectFileName>$(IntDir)</ObjectFileName>
|
||||
</ClCompile>
|
||||
<ResourceCompile>
|
||||
<PreprocessorDefinitions>WIN32;_WINDOWS;_DEBUG;ARCHITECTURE_x86=1;_CRT_SECURE_NO_WARNINGS;CMAKE_INTDIR=\"Debug\";%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<AdditionalIncludeDirectories>%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
</ResourceCompile>
|
||||
<Midl>
|
||||
<AdditionalIncludeDirectories>%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<OutputDirectory>$(ProjectDir)/$(IntDir)</OutputDirectory>
|
||||
<HeaderFileName>%(Filename).h</HeaderFileName>
|
||||
<TypeLibraryName>%(Filename).tlb</TypeLibraryName>
|
||||
<InterfaceIdentifierFileName>%(Filename)_i.c</InterfaceIdentifierFileName>
|
||||
<ProxyFileName>%(Filename)_p.c</ProxyFileName>
|
||||
</Midl>
|
||||
<PostBuildEvent>
|
||||
<Message>
|
||||
</Message>
|
||||
<Command>copy $(ProjectDir)\..\packages\Azure.IoT.Gateway.SDK.Net.2017.1.13.2\build\x86\*.dll $(OutDir)</Command>
|
||||
</PostBuildEvent>
|
||||
<Link>
|
||||
<AdditionalOptions> /machine:X86 /guard:cf %(AdditionalOptions)</AdditionalOptions>
|
||||
<AdditionalDependencies>kernel32.lib;user32.lib;gdi32.lib;winspool.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;comdlg32.lib;advapi32.lib;crypt32.lib;winhttp.lib;secur32.lib;ws2_32.lib;mswsock.lib;rpcrt4.lib;</AdditionalDependencies>
|
||||
<AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
|
||||
<GenerateDebugInformation>Debug</GenerateDebugInformation>
|
||||
<IgnoreSpecificDefaultLibraries>%(IgnoreSpecificDefaultLibraries)</IgnoreSpecificDefaultLibraries>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<Version>
|
||||
</Version>
|
||||
</Link>
|
||||
<ProjectReference>
|
||||
<LinkLibraryDependencies>false</LinkLibraryDependencies>
|
||||
</ProjectReference>
|
||||
<CustomBuildStep>
|
||||
<Command>
|
||||
</Command>
|
||||
</CustomBuildStep>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<ClCompile>
|
||||
<AdditionalOptions> /guard:cf %(AdditionalOptions)</AdditionalOptions>
|
||||
<AdditionalIncludeDirectories>%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<AssemblerListingLocation>Release/</AssemblerListingLocation>
|
||||
<CompileAs>CompileAsC</CompileAs>
|
||||
<ExceptionHandling>
|
||||
</ExceptionHandling>
|
||||
<InlineFunctionExpansion>AnySuitable</InlineFunctionExpansion>
|
||||
<Optimization>MaxSpeed</Optimization>
|
||||
<PrecompiledHeader>NotUsing</PrecompiledHeader>
|
||||
<RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<PreprocessorDefinitions>WIN32;_WINDOWS;NDEBUG;ARCHITECTURE_x86=1;_CRT_SECURE_NO_WARNINGS;CMAKE_INTDIR="Release";%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ObjectFileName>$(IntDir)</ObjectFileName>
|
||||
<DebugInformationFormat>
|
||||
</DebugInformationFormat>
|
||||
</ClCompile>
|
||||
<ResourceCompile>
|
||||
<PreprocessorDefinitions>WIN32;_WINDOWS;NDEBUG;ARCHITECTURE_x86=1;_CRT_SECURE_NO_WARNINGS;CMAKE_INTDIR=\"Release\";%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<AdditionalIncludeDirectories>%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
</ResourceCompile>
|
||||
<Midl>
|
||||
<AdditionalIncludeDirectories>%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<OutputDirectory>$(ProjectDir)/$(IntDir)</OutputDirectory>
|
||||
<HeaderFileName>%(Filename).h</HeaderFileName>
|
||||
<TypeLibraryName>%(Filename).tlb</TypeLibraryName>
|
||||
<InterfaceIdentifierFileName>%(Filename)_i.c</InterfaceIdentifierFileName>
|
||||
<ProxyFileName>%(Filename)_p.c</ProxyFileName>
|
||||
</Midl>
|
||||
<PostBuildEvent>
|
||||
<Message>
|
||||
</Message>
|
||||
<Command>copy $(ProjectDir)\..\packages\Azure.IoT.Gateway.SDK.Net.2017.1.13.2\build\x86\*.dll $(OutDir)</Command>
|
||||
</PostBuildEvent>
|
||||
<Link>
|
||||
<AdditionalOptions> /machine:X86 /guard:cf %(AdditionalOptions)</AdditionalOptions>
|
||||
<AdditionalDependencies>kernel32.lib;user32.lib;gdi32.lib;winspool.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;comdlg32.lib;advapi32.lib;crypt32.lib;winhttp.lib;secur32.lib;ws2_32.lib;mswsock.lib;rpcrt4.lib</AdditionalDependencies>
|
||||
<AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
|
||||
<GenerateDebugInformation>No</GenerateDebugInformation>
|
||||
<IgnoreSpecificDefaultLibraries>%(IgnoreSpecificDefaultLibraries)</IgnoreSpecificDefaultLibraries>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<Version>
|
||||
</Version>
|
||||
</Link>
|
||||
<ProjectReference>
|
||||
<LinkLibraryDependencies>false</LinkLibraryDependencies>
|
||||
</ProjectReference>
|
||||
<CustomBuildStep>
|
||||
<Command>
|
||||
</Command>
|
||||
</CustomBuildStep>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="main.c" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
</ImportGroup>
|
||||
</Project>
|
|
@ -1,53 +0,0 @@
|
|||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
#include <windows.h>
|
||||
#include <stdio.h>
|
||||
|
||||
typedef void* (__cdecl *P_GATEWAY_CREATE_FROM_JSON)(char*);
|
||||
typedef void (__cdecl *P_GATEWAY_DESTROY)(void*);
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
if (argc != 2)
|
||||
{
|
||||
printf("usage: dotnet_binding_sample configFile\n");
|
||||
printf("where configFile is the name of the file that contains the Gateway configuration\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
HINSTANCE hDLL = LoadLibraryA("gateway.dll");
|
||||
if (!hDLL)
|
||||
{
|
||||
printf("failed to load gateway.dll, error: %d\n", GetLastError());
|
||||
return 1;
|
||||
}
|
||||
|
||||
P_GATEWAY_CREATE_FROM_JSON Gateway_CreateFromJson = (P_GATEWAY_CREATE_FROM_JSON) GetProcAddress(hDLL, "Gateway_CreateFromJson");
|
||||
if (!Gateway_CreateFromJson)
|
||||
{
|
||||
printf("failed to load function Gateway_CreateFromJson, error: %d\n", GetLastError());
|
||||
return 1;
|
||||
}
|
||||
|
||||
void* pGateway = Gateway_CreateFromJson(argv[1]);
|
||||
if (!pGateway)
|
||||
{
|
||||
printf("failed to create the gateway from JSON\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
printf("gateway successfully created from JSON\n");
|
||||
printf("gateway will run until ENTER is pressed\n");
|
||||
getchar();
|
||||
|
||||
P_GATEWAY_DESTROY Gateway_Destroy = (P_GATEWAY_DESTROY) GetProcAddress(hDLL, "Gateway_Destroy");
|
||||
if (Gateway_Destroy)
|
||||
{
|
||||
Gateway_Destroy(pGateway);
|
||||
}
|
||||
|
||||
FreeLibrary(hDLL);
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,279 @@
|
|||
@REM Copyright (c) Microsoft. All rights reserved.
|
||||
@REM Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
@setlocal EnableExtensions EnableDelayedExpansion
|
||||
@echo off
|
||||
|
||||
set current-path=%~dp0
|
||||
rem // remove trailing slash
|
||||
set current-path=%current-path:~0,-1%
|
||||
|
||||
set repo-root=%current-path%\..
|
||||
rem // resolve to fully qualified path
|
||||
for %%i in ("%repo-root%") do set repo-root=%%~fi
|
||||
|
||||
set build-sdk-root=%repo-root%\..\azure-iot-gateway-sdk
|
||||
for %%i in ("%build-sdk-root%") do set build-sdk-root=%%~fi
|
||||
|
||||
rem ----------------------------------------------------------------------------
|
||||
rem -- parse script arguments
|
||||
rem ----------------------------------------------------------------------------
|
||||
|
||||
rem // default build options
|
||||
set build-clean=
|
||||
set build-configs=
|
||||
set build-platform=Win32
|
||||
if "%PROCESSOR_ARCHITECTURE%" == "AMD64" set build-platform=x64
|
||||
set build-runtime=
|
||||
set build-root=%repo-root%\build
|
||||
set build-rel-root=%build-root%\release
|
||||
|
||||
:args-loop
|
||||
if "%1" equ "" goto :args-done
|
||||
if "%1" equ "--config" goto :arg-build-config
|
||||
if "%1" equ "-C" goto :arg-build-config
|
||||
if "%1" equ "--clean" goto :arg-build-clean
|
||||
if "%1" equ "-c" goto :arg-build-clean
|
||||
if "%1" equ "--output" goto :arg-build-rel-root
|
||||
if "%1" equ "-o" goto :arg-build-rel-root
|
||||
if "%1" equ "--sdk-root" goto :arg-sdk-root-folder
|
||||
if "%1" equ "-i" goto :arg-sdk-root-folder
|
||||
if "%1" equ "--platform" goto :arg-build-platform
|
||||
if "%1" equ "-p" goto :arg-build-platform
|
||||
if "%1" equ "--runtime" goto :arg-build-runtime
|
||||
if "%1" equ "-r" goto :arg-build-runtime
|
||||
call :usage && exit /b 1
|
||||
|
||||
:arg-build-clean
|
||||
set build-clean=1
|
||||
goto :args-continue
|
||||
|
||||
:arg-build-config
|
||||
shift
|
||||
if "%1" equ "" call :usage && exit /b 1
|
||||
set build-configs=%build-configs%%1
|
||||
goto :args-continue
|
||||
|
||||
:arg-sdk-root-folder
|
||||
shift
|
||||
if "%1" equ "" call :usage && exit /b 1
|
||||
set build-sdk-root=%1
|
||||
goto :args-continue
|
||||
|
||||
:arg-build-platform
|
||||
shift
|
||||
if "%1" equ "" call :usage && exit /b 1
|
||||
set build-platform=%1
|
||||
goto :args-continue
|
||||
|
||||
:arg-build-runtime
|
||||
shift
|
||||
if "%1" equ "" call :usage && exit /b 1
|
||||
set build-runtime=-r %1
|
||||
goto :args-continue
|
||||
|
||||
:arg-build-rel-root
|
||||
shift
|
||||
if "%1" equ "" call :usage && exit /b 1
|
||||
set build-rel-root=%1
|
||||
|
||||
goto :args-continue
|
||||
:args-continue
|
||||
shift
|
||||
goto :args-loop
|
||||
|
||||
:args-done
|
||||
call dotnet --version
|
||||
if not !ERRORLEVEL! == 0 echo No dotnet installed, install first... && exit /b 1
|
||||
if not exist "%build-sdk-root%\tools\build.cmd" echo no sdk installed at %build-sdk-root%, only building module... && set build-sdk-root=
|
||||
if "%build-configs%" == "" set build-configs=Release Debug
|
||||
|
||||
rem // Start script
|
||||
echo Building %build-configs%...
|
||||
if not "%build-clean%" == "" (
|
||||
echo Cleaning previous build output...
|
||||
call :rmdir-force %build-root%
|
||||
)
|
||||
if not exist %build-root% mkdir %build-root%
|
||||
call :sdk-build
|
||||
if not !ERRORLEVEL! == 0 echo Failures during sdk build... && exit /b !ERRORLEVEL!
|
||||
call :module-build
|
||||
if not !ERRORLEVEL! == 0 echo Failures during dotnet build... && exit /b !ERRORLEVEL!
|
||||
call :release-all
|
||||
if not !ERRORLEVEL! == 0 echo Failures building release... && exit /b !ERRORLEVEL!
|
||||
goto :build-done
|
||||
|
||||
rem -----------------------------------------------------------------------------
|
||||
rem -- build the sdk
|
||||
rem -----------------------------------------------------------------------------
|
||||
:sdk-build
|
||||
if "%build-sdk-root%" == "" goto :eof
|
||||
rem // Build the sdk for all configurations
|
||||
for %%c in (%build-configs%) do (
|
||||
pushd "%build-sdk-root%\tools
|
||||
call :sdk-build-and-test %%c
|
||||
popd
|
||||
if not !ERRORLEVEL! == 0 exit /b !ERRORLEVEL!
|
||||
)
|
||||
goto :eof
|
||||
rem // Build the sdk for 1 configuration
|
||||
:sdk-build-and-test
|
||||
if /I not "%~1" == "Release" if /I not "%~1" == "Debug" if /I not "%~1" == "MinSizeRel" if /I not "%~1" == "RelWithDebInfo" goto :eof
|
||||
rem // If incremental, check if we had a successful build before...
|
||||
if exist %build-root%\sdk\%build-platform%-%~1.done goto :eof
|
||||
rem // Clean bindings project output
|
||||
pushd %build-sdk-root%\bindings
|
||||
for /f %%i in ('dir /b /s project.json') do call :dotnet-project-clean "%%i"
|
||||
popd
|
||||
rem // First build the dotnetcore binding for configuration. TODO: Remove once build script is fixed.
|
||||
call build_dotnet_core.cmd --config %~1
|
||||
rem // Force clean cmake output and install-deps to avoid errors.
|
||||
call :rmdir-force %build-sdk-root%\build
|
||||
call :rmdir-force %build-sdk-root%\install-deps
|
||||
rem // Build sdk
|
||||
echo Building SDK (%~1) ...
|
||||
call build.cmd --config %~1 --platform %build-platform% --enable-dotnet-core-binding --disable-ble-module
|
||||
echo Finished building SDK (%~1)
|
||||
if not !ERRORLEVEL! == 0 exit /b !ERRORLEVEL!
|
||||
rem // Copy build output over and mark it as successfully built.
|
||||
if not exist "%build-root%\sdk\%build-platform%" mkdir "%build-root%\sdk\%build-platform%"
|
||||
xcopy /e /i /y /q "%build-sdk-root%\build" "%build-root%\sdk\%build-platform%"
|
||||
if not !ERRORLEVEL! == 0 exit /b !ERRORLEVEL!
|
||||
echo %~1 >> %build-root%\sdk\%build-platform%-%~1.done
|
||||
goto :eof
|
||||
|
||||
rem -----------------------------------------------------------------------------
|
||||
rem -- build module
|
||||
rem -----------------------------------------------------------------------------
|
||||
:module-build
|
||||
|
||||
rem // Restore packages
|
||||
:dotnet-restore
|
||||
pushd %repo-root%
|
||||
call dotnet restore
|
||||
popd
|
||||
if not !ERRORLEVEL! == 0 exit /b !ERRORLEVEL!
|
||||
rem // Build and publish all specified configurations
|
||||
for %%c in (%build-configs%) do (
|
||||
call :dotnet-build-and-publish %%c
|
||||
if not !ERRORLEVEL! == 0 exit /b !ERRORLEVEL!
|
||||
)
|
||||
goto :eof
|
||||
rem // Build module and publish for 1 configuration
|
||||
:dotnet-build-and-publish
|
||||
if /I not "%~1" == "Release" if /I not "%~1" == "Debug" if /I not "%~1" == "Signed" goto :eof
|
||||
rem // Clean
|
||||
:dotnet-clean
|
||||
if "%build-clean%" == "" goto :dotnet-build
|
||||
call :dotnet-project-clean %repo-root%\src\Opc.Ua.Client.Module
|
||||
call :dotnet-project-clean %repo-root%\bld\publish
|
||||
rem // Build
|
||||
:dotnet-build
|
||||
pushd %repo-root%\src\Opc.Ua.Client.Module
|
||||
call dotnet build %build-runtime% -c %~1
|
||||
popd
|
||||
if not !ERRORLEVEL! == 0 exit /b !ERRORLEVEL!
|
||||
rem // Publish all assemblies using publish dummy exe
|
||||
:dotnet-publish
|
||||
pushd %repo-root%\bld\publish
|
||||
call dotnet publish %build-runtime% -c %~1 -o "%build-root%\module\%~1"
|
||||
popd
|
||||
if not !ERRORLEVEL! == 0 exit /b !ERRORLEVEL!
|
||||
goto :eof
|
||||
|
||||
rem // Clean a project
|
||||
:dotnet-project-clean
|
||||
pushd "%~dp1"
|
||||
call :rmdir-force bin
|
||||
call :rmdir-force obj
|
||||
popd
|
||||
goto :eof
|
||||
|
||||
rem -----------------------------------------------------------------------------
|
||||
rem -- Copy everything into a final release folder
|
||||
rem -----------------------------------------------------------------------------
|
||||
:release-all
|
||||
for %%c in (%build-configs%) do (
|
||||
call :release-binaries %%c
|
||||
if not !ERRORLEVEL! == 0 exit /b !ERRORLEVEL!
|
||||
)
|
||||
goto :eof
|
||||
:release-binaries
|
||||
rem // Clean release folder
|
||||
if not exist "%build-rel-root%\%~1" mkdir "%build-rel-root%\%~1"
|
||||
del /f /q "%build-rel-root%\%~1\*"
|
||||
|
||||
rem // Flatten runtimes for windows (TODO: Should be done by loader)
|
||||
xcopy /y /i /q "%build-root%\module\%~1" "%build-rel-root%\%~1"
|
||||
pushd "%build-root%\module\%~1\runtimes\win"
|
||||
for /f %%i in ('dir /b /s *.dll') do copy /y "%%i" "%build-rel-root%\%~1"
|
||||
popd
|
||||
pushd "%build-root%\module\%~1\runtimes\win7"
|
||||
for /f %%i in ('dir /b /s *.dll') do copy /y "%%i" "%build-rel-root%\%~1"
|
||||
popd
|
||||
rem // Copy configuration json
|
||||
copy /y "%repo-root%\samples\*.json" "%build-rel-root%\%~1"
|
||||
if not !ERRORLEVEL! == 0 exit /b !ERRORLEVEL!
|
||||
rem // Delete unnecessary publish.dll and .pdb
|
||||
pushd "%build-rel-root%\%~1"
|
||||
del /q /f publish.*
|
||||
popd
|
||||
if not !ERRORLEVEL! == 0 exit /b !ERRORLEVEL!
|
||||
|
||||
if "%build-sdk-root%" == "" goto :eof
|
||||
if not !ERRORLEVEL! == 0 exit /b !ERRORLEVEL!
|
||||
rem // Copy a sample gw host.exe, iothub.dll, iothub_client.dll
|
||||
pushd %build-root%\sdk\%build-platform%
|
||||
xcopy /e /y /i /q "samples\azure_functions_sample\%~1" "%build-rel-root%\%~1"
|
||||
popd
|
||||
if not !ERRORLEVEL! == 0 exit /b !ERRORLEVEL!
|
||||
pushd %build-root%\sdk\%build-platform%
|
||||
xcopy /e /y /i /q "samples\dotnet_core_module_sample\%~1" "%build-rel-root%\%~1"
|
||||
popd
|
||||
if not !ERRORLEVEL! == 0 exit /b !ERRORLEVEL!
|
||||
pushd %build-root%\sdk\%build-platform%
|
||||
xcopy /e /y /i /q "modules\iothub\%~1" "%build-rel-root%\%~1"
|
||||
popd
|
||||
if not !ERRORLEVEL! == 0 exit /b !ERRORLEVEL!
|
||||
rem // rename sample host.exe and delete unneeded files
|
||||
pushd "%build-rel-root%\%~1"
|
||||
copy /y dotnet_core_module_sample.* sample_gateway.*
|
||||
del /q /f dotnet_core_module_sample.*
|
||||
del /q /f printermodule.*
|
||||
del /q /f sensormodule.*
|
||||
popd
|
||||
if not !ERRORLEVEL! == 0 exit /b !ERRORLEVEL!
|
||||
goto :eof
|
||||
|
||||
rem -----------------------------------------------------------------------------
|
||||
rem -- subroutines
|
||||
rem -----------------------------------------------------------------------------
|
||||
|
||||
:rmdir-force
|
||||
set _attempt=0
|
||||
:try-rmdir
|
||||
if not exist %1 goto :done-rmdir
|
||||
set /a _attempt+=1
|
||||
if !_attempt! == 30 goto :done-rmdir
|
||||
echo Removing %~1 (%_attempt%)...
|
||||
rmdir /s /q %1
|
||||
goto :try-rmdir
|
||||
:done-rmdir
|
||||
set _attempt=
|
||||
goto :eof
|
||||
|
||||
:build-done
|
||||
echo ... Success!
|
||||
goto :eof
|
||||
|
||||
:usage
|
||||
echo build.cmd [options]
|
||||
echo options:
|
||||
echo -c --clean Build clean (Removes previous build output).
|
||||
echo -C --config ^<value^> [Debug, Release] build configuration
|
||||
echo -r --runtime ^<value^> [win] The runtime to build module for.
|
||||
echo -i --sdk-root ^<value^> [../azure-iot-gateway-sdk] Gateway SDK repo root.
|
||||
echo -p --platform ^<value^> [Win32] build platform (e.g. Win32, x64, ...).
|
||||
echo -o --output ^<value^> [/build/release] Root in which to place release.
|
||||
goto :eof
|
||||
|
|
@ -0,0 +1,220 @@
|
|||
#!/bin/bash
|
||||
# Copyright (c) Microsoft. All rights reserved.
|
||||
# Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
set -e
|
||||
|
||||
repo_root=$(cd "$(dirname "$0")/.." && pwd)
|
||||
|
||||
skip_unittests=OFF
|
||||
skip_dotnet=0
|
||||
use_zlog=OFF
|
||||
|
||||
build_root="${repo_root}/build"
|
||||
build_rel_root="${build_root}/release"
|
||||
build_sdk_root="$(cd "$(dirname "$0")/../.." && pwd)/azure-iot-gateway-sdk"
|
||||
build_clean=0
|
||||
build_pack_only=0
|
||||
build_configs=()
|
||||
build_runtime=
|
||||
|
||||
usage ()
|
||||
{
|
||||
echo "build.sh [options]"
|
||||
echo "options"
|
||||
echo " -c --clean Build clean (Removes previous build output)."
|
||||
echo " -C --config ^<value^> [Debug, Release] build configuration"
|
||||
echo " -r --runtime ^<value^> Runtime to publish for."
|
||||
echo " -i --sdk-root ^<value^> [../azure-iot-gateway-sdk] Gateway SDK repo root."
|
||||
echo " -o --output ^<value^> [/build/release] Root in which to place release."
|
||||
echo " -x --xtrace print a trace of each command"
|
||||
exit 1
|
||||
}
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# -- Parse arguments
|
||||
# -----------------------------------------------------------------------------
|
||||
process_args ()
|
||||
{
|
||||
save_next_arg=0
|
||||
for arg in $*; do
|
||||
if [ $save_next_arg == 1 ]; then
|
||||
build_rel_root="$arg"
|
||||
save_next_arg=0
|
||||
elif [ $save_next_arg == 2 ]; then
|
||||
build_configs+=("$arg")
|
||||
save_next_arg=0
|
||||
elif [ $save_next_arg == 3 ]; then
|
||||
build_sdk_root="$arg"
|
||||
save_next_arg=0
|
||||
elif [ $save_next_arg == 4 ]; then
|
||||
build_runtime="$arg"
|
||||
save_next_arg=0
|
||||
else
|
||||
case "$arg" in
|
||||
-x | --xtrace)
|
||||
set -x;;
|
||||
-o | --build-root)
|
||||
save_next_arg=1;;
|
||||
-C | --config)
|
||||
save_next_arg=2;;
|
||||
-c | --clean)
|
||||
build_clean=1;;
|
||||
-i | --sdk-root)
|
||||
save_next_arg=3;;
|
||||
-r | --runtime)
|
||||
save_next_arg=4;;
|
||||
*)
|
||||
usage;;
|
||||
esac
|
||||
fi
|
||||
done
|
||||
|
||||
if [ ! -e "${build_sdk_root}/tools/build.sh" ]; then
|
||||
echo "No gateway sdk installed at ${build_sdk_root}... "
|
||||
build_sdk_root=
|
||||
fi
|
||||
}
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# -- build the sdk
|
||||
# -----------------------------------------------------------------------------
|
||||
sdk_build()
|
||||
{
|
||||
if [ -z "${build_sdk_root}" ]; then
|
||||
echo "Skipping sdk build..."
|
||||
else
|
||||
echo -e "\033[1mBuilding sdk...\033[0m"
|
||||
for c in ${build_configs[@]}; do
|
||||
if [ -e "${build_root}/sdk/${c}.done" ]; then
|
||||
echo "Skipping building sdk ${c}..."
|
||||
else
|
||||
echo -e "\033[1m ${c}...\033[0m"
|
||||
|
||||
mkdir -p "${build_root}/sdk/${c}"
|
||||
|
||||
# linux script in tools is totally hosed, build directly
|
||||
pushd "${build_sdk_root}/bindings/dotnetcore/dotnet-core-binding" > /dev/null
|
||||
dotnet restore \
|
||||
|| return $?
|
||||
dotnet build -c ${c} -r ${build_runtime} \
|
||||
./Microsoft.Azure.IoT.Gateway \
|
||||
./PrinterModule \
|
||||
./SensorModule \
|
||||
|| return $?
|
||||
popd > /dev/null
|
||||
|
||||
rm -r -f "${build_sdk_root}/build" || \
|
||||
return 1
|
||||
rm -r -f "${build_sdk_root}/install-deps" || \
|
||||
return 1
|
||||
|
||||
pushd ${build_sdk_root}/tools > /dev/null
|
||||
( ./build.sh --config ${c} --enable-dotnet-core-binding --disable-ble-module ) || \
|
||||
return 1
|
||||
popd > /dev/null
|
||||
|
||||
cp -r "${build_sdk_root}/build/"* "${build_root}/sdk/${c}" || \
|
||||
return 1
|
||||
|
||||
echo "${c}" >> "${build_root}/sdk/${c}.done"
|
||||
fi
|
||||
done
|
||||
fi
|
||||
return 0
|
||||
}
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# -- build module and publish
|
||||
# -----------------------------------------------------------------------------
|
||||
module_build()
|
||||
{
|
||||
pushd "${repo_root}" > /dev/null
|
||||
echo -e "\033[1mBuilding module...\033[0m"
|
||||
dotnet restore || exit 1
|
||||
for c in ${build_configs[@]}; do
|
||||
echo -e "\033[1m ${c}...\033[0m"
|
||||
|
||||
mkdir -p "${build_root}/module/${c}"
|
||||
|
||||
dotnet build -c ${c} --framework netstandard1.6 \
|
||||
-r ${build_runtime} ./src/Opc.Ua.Client.Module \
|
||||
|| return $?
|
||||
|
||||
dotnet publish -c ${c} -o "${build_root}/module/${c}" --framework netcoreapp1.1 \
|
||||
-r ${build_runtime} ./bld/publish \
|
||||
|| return $?
|
||||
|
||||
done
|
||||
popd > /dev/null
|
||||
return 0
|
||||
}
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# -- Copy everything into a final release folder
|
||||
# -----------------------------------------------------------------------------
|
||||
release_all()
|
||||
{
|
||||
for c in ${build_configs[@]}; do
|
||||
rm -rf "${build_rel_root}/${c}"
|
||||
mkdir -p "${build_rel_root}/${c}"
|
||||
|
||||
pushd "${build_root}/module/${c}" > /dev/null
|
||||
find . -type f -print0 | xargs -0 -I%%% cp %%% "${build_rel_root}/${c}" || \
|
||||
return 1
|
||||
popd > /dev/null
|
||||
|
||||
pushd "${build_rel_root}/${c}" > /dev/null
|
||||
rm -f publish.*
|
||||
popd > /dev/null
|
||||
|
||||
if [ "${build_sdk_root}" ]; then
|
||||
pushd "${build_root}/sdk/${c}" > /dev/null
|
||||
find . -wholename *dotnetcore.so -type f -print0 | xargs -0 \
|
||||
-I%%% cp %%% "${build_rel_root}/${c}" || \
|
||||
return 1
|
||||
find . -wholename *iothub*.so -type f -print0 | xargs -0 \
|
||||
-I%%% cp %%% "${build_rel_root}/${c}" || \
|
||||
return 1
|
||||
find . -wholename *gateway*.so -type f -print0 | xargs -0 \
|
||||
-I%%% cp %%% "${build_rel_root}/${c}" || \
|
||||
return 1
|
||||
cp -r "samples/dotnet_core_module_sample/dotnet_core_module_sample" \
|
||||
"${build_rel_root}/${c}/sample_gateway" || \
|
||||
return 1
|
||||
popd > /dev/null
|
||||
fi
|
||||
cp -r "${repo_root}/samples/"*.json "${build_rel_root}/${c}" || \
|
||||
return 1
|
||||
done
|
||||
return 0
|
||||
}
|
||||
|
||||
pushd "${repo_root}" > /dev/null
|
||||
process_args $*
|
||||
|
||||
if [ -z "$build_runtime" ]; then
|
||||
build_runtime=ubuntu.16.10
|
||||
fi
|
||||
|
||||
if [ -z "$build_configs" ]; then
|
||||
build_configs=(Debug Release)
|
||||
fi
|
||||
|
||||
echo "Building ${build_configs[@]}..."
|
||||
|
||||
if [ $build_clean == 1 ]; then
|
||||
echo "Cleaning previous build output..."
|
||||
rm -r -f "${build_root}"
|
||||
fi
|
||||
|
||||
mkdir -p "${build_root}"
|
||||
|
||||
sdk_build || exit 1
|
||||
module_build || exit 1
|
||||
release_all || exit 1
|
||||
|
||||
popd > /dev/null
|
||||
|
||||
[ $? -eq 0 ] || exit $?
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
namespace Opc.Ua.Client
|
||||
{
|
||||
class Program
|
||||
{
|
||||
static void Main(string[] args)
|
||||
{
|
||||
var module = new SampleModule();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
{
|
||||
"buildOptions": {
|
||||
"emitEntryPoint": true
|
||||
},
|
||||
"dependencies": {
|
||||
"Opc.Ua.Client.Module": "0.1.6"
|
||||
},
|
||||
"frameworks": {
|
||||
"netcoreapp1.1": {
|
||||
"dependencies": {
|
||||
"Microsoft.NETCore.App": {
|
||||
"type": "platform",
|
||||
"version": "1.1.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,35 +0,0 @@
|
|||
{
|
||||
"modules": [
|
||||
{
|
||||
"name": "opc_ua",
|
||||
"loader": {
|
||||
"name": "dotnet",
|
||||
"entrypoint": {
|
||||
"assembly.name": "Opc.Ua.Client.SampleModule",
|
||||
"entry.type": "Opc.Ua.Client.SampleModule"
|
||||
}
|
||||
},
|
||||
"args": "<DeviceID>;<SharedAccessKey>"
|
||||
},
|
||||
{
|
||||
"name": "IoTHub",
|
||||
"loader": {
|
||||
"name": "native",
|
||||
"entrypoint": {
|
||||
"module.path": "iothub.dll"
|
||||
}
|
||||
},
|
||||
"args": {
|
||||
"IoTHubName": "<IoTHubName>",
|
||||
"IoTHubSuffix": "azure-devices.net",
|
||||
"Transport": "AMQP"
|
||||
}
|
||||
}
|
||||
],
|
||||
"links": [
|
||||
{
|
||||
"source": "opc_ua",
|
||||
"sink": "IoTHub"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"projects": [
|
||||
"src", "bld/publish"
|
||||
]
|
||||
}
|
|
@ -1,8 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="Azure.IoT.Gateway.SDK.Net" version="2017.1.13.2" targetFramework="net46" />
|
||||
<package id="Newtonsoft.Json" version="9.0.1" targetFramework="net461" />
|
||||
<package id="OPCFoundation.NetStandard.Opc.Ua.Core" version="0.1.1" targetFramework="net46" />
|
||||
<package id="OPCFoundation.NetStandard.Opc.Ua.SDK" version="0.1.1" targetFramework="net46" />
|
||||
<package id="Portable.BouncyCastle" version="1.8.1.2" targetFramework="net46" />
|
||||
</packages>
|
|
@ -0,0 +1,63 @@
|
|||
{
|
||||
"modules": [
|
||||
{
|
||||
"name": "opc_ua",
|
||||
"loader": {
|
||||
"name": "dotnetcore",
|
||||
"entrypoint": {
|
||||
"assembly.name": "Opc.Ua.Client.Module",
|
||||
"entry.type": "Opc.Ua.Client.SampleModule"
|
||||
}
|
||||
},
|
||||
"args": {
|
||||
"Configuration": {
|
||||
"ApplicationName": "Opc.Ua.Client.SampleModule",
|
||||
"ApplicationType": "Client",
|
||||
"ApplicationUri": "urn:localhost:OPCFoundation:SampleModule",
|
||||
"SecurityConfiguration": {
|
||||
"ApplicationCertificate": {}
|
||||
}
|
||||
},
|
||||
"Subscriptions": [
|
||||
{
|
||||
"Id": "<DeviceID>",
|
||||
"SharedAccessKey": "<SharedAccessKey>",
|
||||
"ServerUrl": "opc.tcp://<hostname>:51210/UA/SampleServer",
|
||||
"MinimumSecurityLevel": 0,
|
||||
"MinimumSecurityMode": "SignAndEncrypt",
|
||||
"PublishingInterval": 400,
|
||||
"MonitoredItems": [
|
||||
{
|
||||
"StartNodeId": "i=2258",
|
||||
"NodeClass": 2,
|
||||
"DisplayName": "ServerStatusCurrentTime",
|
||||
"DiscardOldest": false
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "IoTHub",
|
||||
"loader": {
|
||||
"name": "native",
|
||||
"entrypoint": {
|
||||
"module.path": "<Choose: iothub.dll / libiothub.so>"
|
||||
}
|
||||
},
|
||||
"args": {
|
||||
"IoTHubName": "<IoTHubName>",
|
||||
"IoTHubSuffix": "azure-devices.net",
|
||||
"Transport": "AMQP"
|
||||
}
|
||||
}
|
||||
],
|
||||
"links": [
|
||||
{
|
||||
"source": "opc_ua",
|
||||
"sink": "IoTHub"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
Двоичный файл не отображается.
|
@ -0,0 +1,544 @@
|
|||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
namespace Opc.Ua.Client
|
||||
{
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using System.Runtime.Serialization;
|
||||
using System.Text;
|
||||
using Newtonsoft.Json;
|
||||
using Microsoft.Azure.IoT.Gateway;
|
||||
using Ua;
|
||||
using System.Security.Cryptography.X509Certificates;
|
||||
|
||||
/// <summary>
|
||||
/// Sample Gateway module - acts as Opc.Ua publisher and publishing server
|
||||
/// </summary>
|
||||
public class SampleModule : IGatewayModule, IGatewayModuleStart
|
||||
{
|
||||
/// <summary>
|
||||
/// Create module, throws if configuration is bad
|
||||
/// </summary>
|
||||
/// <param name="broker"></param>
|
||||
/// <param name="configuration"></param>
|
||||
public void Create(Broker broker, byte[] configuration)
|
||||
{
|
||||
_broker = broker;
|
||||
|
||||
string configString = Encoding.UTF8.GetString(configuration);
|
||||
|
||||
// Deserialize from configuration string
|
||||
_configuration = JsonConvert.DeserializeObject<SampleConfiguration>(configString);
|
||||
|
||||
foreach (var session in _configuration.Subscriptions)
|
||||
{
|
||||
session.Module = this;
|
||||
}
|
||||
|
||||
Console.WriteLine("Opc.Ua.Client.SampleModule: created.");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Disconnect and dispose all sessions
|
||||
/// </summary>
|
||||
public void Destroy()
|
||||
{
|
||||
foreach (var session in _configuration.Subscriptions)
|
||||
{
|
||||
// Disconnect and dispose
|
||||
session.Dispose();
|
||||
}
|
||||
// Then gc.
|
||||
_configuration.Subscriptions.Clear();
|
||||
Console.WriteLine("Opc.Ua.Client.SampleModule: destroyed.");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Receive message from broker
|
||||
/// </summary>
|
||||
/// <param name="received_message"></param>
|
||||
public void Receive(Message received_message)
|
||||
{
|
||||
// No-op
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Publish message to bus
|
||||
/// </summary>
|
||||
/// <param name="message"></param>
|
||||
public void Publish(Message message)
|
||||
{
|
||||
if (_broker != null)
|
||||
{
|
||||
_broker.Publish(message);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when gateway starts, establishes the connections to endpoints
|
||||
/// </summary>
|
||||
public void Start()
|
||||
{
|
||||
Console.WriteLine("Opc.Ua.Client.SampleModule: starting...");
|
||||
|
||||
var connections = new List<Task>();
|
||||
foreach (var session in _configuration.Subscriptions)
|
||||
{
|
||||
connections.Add(session.EndpointConnect());
|
||||
}
|
||||
try
|
||||
{
|
||||
Task.WaitAll(connections.ToArray());
|
||||
}
|
||||
catch (AggregateException ae)
|
||||
{
|
||||
foreach (var ex in ae.InnerExceptions)
|
||||
{
|
||||
Console.WriteLine($"Opc.Ua.Client.SampleModule: Could not connect {ex.ToString()}");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($"Opc.Ua.Client.SampleModule: Could not connect {ex.ToString()}");
|
||||
}
|
||||
|
||||
// Wait for all sessions to be connected
|
||||
Console.WriteLine("Opc.Ua.Client.SampleModule: started.");
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Allow access to the opc ua configuration
|
||||
/// </summary>
|
||||
internal ApplicationConfiguration Configuration
|
||||
{
|
||||
get
|
||||
{
|
||||
return _configuration.Configuration;
|
||||
}
|
||||
}
|
||||
|
||||
private Broker _broker;
|
||||
private SampleConfiguration _configuration;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Module configuration object to deserialize / serialize
|
||||
/// </summary>
|
||||
[JsonObject(MemberSerialization.OptIn)]
|
||||
public class SampleConfiguration
|
||||
{
|
||||
/// <summary>
|
||||
/// Opc client configuration
|
||||
/// </summary>
|
||||
[JsonProperty]
|
||||
public ApplicationConfiguration Configuration { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// List of sessions to create on startup
|
||||
/// </summary>
|
||||
[JsonProperty]
|
||||
public List<ServerSession> Subscriptions { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Called when the object is deserialized
|
||||
/// </summary>
|
||||
/// <param name="context"></param>
|
||||
[OnDeserialized]
|
||||
internal void OnDeserializedMethod(StreamingContext context)
|
||||
{
|
||||
// Validate configuration and set reasonable defaults
|
||||
|
||||
Configuration.ApplicationUri = Configuration.ApplicationUri.Replace("localhost", Utils.GetHostName());
|
||||
|
||||
if (Configuration.TransportQuotas == null)
|
||||
Configuration.TransportQuotas = new TransportQuotas { OperationTimeout = 15000 };
|
||||
|
||||
if (Configuration.ClientConfiguration == null)
|
||||
Configuration.ClientConfiguration = new ClientConfiguration();
|
||||
if (Configuration.ServerConfiguration == null)
|
||||
Configuration.ServerConfiguration = new ServerConfiguration();
|
||||
|
||||
if (Configuration.SecurityConfiguration.TrustedPeerCertificates == null)
|
||||
Configuration.SecurityConfiguration.TrustedPeerCertificates = new CertificateTrustList();
|
||||
if (Configuration.SecurityConfiguration.TrustedPeerCertificates.StoreType == null)
|
||||
Configuration.SecurityConfiguration.TrustedPeerCertificates.StoreType =
|
||||
"Directory";
|
||||
if (Configuration.SecurityConfiguration.TrustedPeerCertificates.StorePath == null)
|
||||
Configuration.SecurityConfiguration.TrustedPeerCertificates.StorePath =
|
||||
"OPC Foundation/CertificateStores/UA Applications";
|
||||
|
||||
if (Configuration.SecurityConfiguration.TrustedIssuerCertificates == null)
|
||||
Configuration.SecurityConfiguration.TrustedIssuerCertificates = new CertificateTrustList();
|
||||
if (Configuration.SecurityConfiguration.TrustedIssuerCertificates.StoreType == null)
|
||||
Configuration.SecurityConfiguration.TrustedIssuerCertificates.StoreType =
|
||||
"Directory";
|
||||
if (Configuration.SecurityConfiguration.TrustedIssuerCertificates.StorePath == null)
|
||||
Configuration.SecurityConfiguration.TrustedIssuerCertificates.StorePath =
|
||||
"OPC Foundation/CertificateStores/UA Certificate Authorities";
|
||||
|
||||
if (Configuration.SecurityConfiguration.RejectedCertificateStore == null)
|
||||
Configuration.SecurityConfiguration.RejectedCertificateStore = new CertificateTrustList();
|
||||
if (Configuration.SecurityConfiguration.RejectedCertificateStore.StoreType == null)
|
||||
Configuration.SecurityConfiguration.RejectedCertificateStore.StoreType =
|
||||
"Directory";
|
||||
if (Configuration.SecurityConfiguration.RejectedCertificateStore.StorePath == null)
|
||||
Configuration.SecurityConfiguration.RejectedCertificateStore.StorePath =
|
||||
"OPC Foundation/CertificateStores/RejectedCertificates";
|
||||
|
||||
if (Configuration.SecurityConfiguration.ApplicationCertificate == null)
|
||||
Configuration.SecurityConfiguration.ApplicationCertificate = new CertificateIdentifier();
|
||||
if (Configuration.SecurityConfiguration.ApplicationCertificate.StoreType == null)
|
||||
Configuration.SecurityConfiguration.ApplicationCertificate.StoreType =
|
||||
"X509Store";
|
||||
if (Configuration.SecurityConfiguration.ApplicationCertificate.StorePath == null)
|
||||
Configuration.SecurityConfiguration.ApplicationCertificate.StorePath =
|
||||
"CurrentUser\\UA_MachineDefault";
|
||||
if (Configuration.SecurityConfiguration.ApplicationCertificate.SubjectName == null)
|
||||
Configuration.SecurityConfiguration.ApplicationCertificate.SubjectName =
|
||||
Configuration.ApplicationName;
|
||||
if (Configuration.SecurityConfiguration.ApplicationCertificate.Certificate == null)
|
||||
{
|
||||
X509Certificate2 certificate = CertificateFactory.CreateCertificate(
|
||||
Configuration.SecurityConfiguration.ApplicationCertificate.StoreType,
|
||||
Configuration.SecurityConfiguration.ApplicationCertificate.StorePath,
|
||||
Configuration.ApplicationUri,
|
||||
Configuration.ApplicationName,
|
||||
Configuration.SecurityConfiguration.ApplicationCertificate.SubjectName
|
||||
);
|
||||
Configuration.SecurityConfiguration.ApplicationCertificate.Certificate = certificate;
|
||||
}
|
||||
|
||||
if (Configuration.SecurityConfiguration.ApplicationCertificate.Certificate != null)
|
||||
{
|
||||
Configuration.ApplicationUri = Utils.GetApplicationUriFromCertificate(
|
||||
Configuration.SecurityConfiguration.ApplicationCertificate.Certificate);
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLine("Opc.Ua.Client.SampleModule: WARNING: missing application certificate, using unsecure connection.");
|
||||
}
|
||||
Configuration.Validate(Configuration.ApplicationType).Wait();
|
||||
|
||||
if (Configuration.SecurityConfiguration.AutoAcceptUntrustedCertificates)
|
||||
Configuration.CertificateValidator.CertificateValidation +=
|
||||
new CertificateValidationEventHandler(CertificateValidator_CertificateValidation);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Auto accept certificates
|
||||
/// </summary>
|
||||
/// <param name="validator"></param>
|
||||
/// <param name="e"></param>
|
||||
private void CertificateValidator_CertificateValidation(CertificateValidator validator, CertificateValidationEventArgs e)
|
||||
{
|
||||
if (e.Error.StatusCode == StatusCodes.BadCertificateUntrusted)
|
||||
{
|
||||
e.Accept = true;
|
||||
Console.WriteLine($"Opc.Ua.Client.SampleModule: WARNING: Auto-accepting certificate: {e.Certificate.Subject}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A server session contains a subscription for a list of monitored items
|
||||
/// </summary>
|
||||
[JsonObject(MemberSerialization.OptIn)]
|
||||
public class ServerSession : IDisposable
|
||||
{
|
||||
/// <summary>
|
||||
/// Url of the Server to connect to
|
||||
/// </summary>
|
||||
[JsonProperty]
|
||||
public Uri ServerUrl { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// SharedAccessKey for device
|
||||
///
|
||||
/// TODO: Security: The shared access key should be stored in secure storage,
|
||||
/// and the device ID can be used as a lookup
|
||||
/// </summary>
|
||||
[JsonProperty]
|
||||
public string SharedAccessKey { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Device id or mappable id for mapper
|
||||
/// </summary>
|
||||
[JsonProperty]
|
||||
public string Id { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Polling interval
|
||||
/// </summary>
|
||||
[JsonProperty]
|
||||
public int PublishingInterval { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Minimum desired Security Level
|
||||
/// </summary>
|
||||
[JsonProperty]
|
||||
public byte MinimumSecurityLevel { get; set; } = 0;
|
||||
|
||||
/// <summary>
|
||||
/// Minimum desired Security mode
|
||||
/// </summary>
|
||||
[JsonProperty]
|
||||
public MessageSecurityMode MinimumSecurityMode { get; set; } = MessageSecurityMode.SignAndEncrypt;
|
||||
|
||||
/// <summary>
|
||||
/// Monitored item configuration
|
||||
/// </summary>
|
||||
[JsonProperty]
|
||||
public List<MonitoredItem> MonitoredItems { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Default constructor
|
||||
/// </summary>
|
||||
public ServerSession()
|
||||
{
|
||||
MonitoredItems = new List<MonitoredItem>();
|
||||
PublishingInterval = 1000;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The Module the session is attached to.
|
||||
/// </summary>
|
||||
internal SampleModule Module { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Called when the object is deserialized
|
||||
/// </summary>
|
||||
/// <param name="context"></param>
|
||||
[OnDeserialized]
|
||||
internal void OnDeserializedMethod(StreamingContext context)
|
||||
{
|
||||
if (MonitoredItems.Count == 0)
|
||||
{
|
||||
throw new Exception("Configuration did not specify monitored items!");
|
||||
}
|
||||
|
||||
if (ServerUrl == null)
|
||||
{
|
||||
throw new Exception("Configuration did not contain an endpoint Uri");
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(Id))
|
||||
{
|
||||
throw new Exception("Configuration did not contain a device name!");
|
||||
}
|
||||
|
||||
MonitoredItems.ForEach(i => i.Notification += MonitoredItem_Notification);
|
||||
}
|
||||
|
||||
public async Task EndpointConnect()
|
||||
{
|
||||
var endpointCollection = DiscoverEndpoints(Module.Configuration, ServerUrl, 60);
|
||||
var selectedEndpoints = new List<EndpointDescription>();
|
||||
|
||||
// Select endpoints
|
||||
foreach (EndpointDescription endpoint in endpointCollection)
|
||||
{
|
||||
if (endpoint.TransportProfileUri == Profiles.UaTcpTransport &&
|
||||
endpoint.SecurityLevel >= MinimumSecurityLevel &&
|
||||
endpoint.SecurityMode >= MinimumSecurityMode)
|
||||
{
|
||||
selectedEndpoints.Add(endpoint);
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Sort, but descending with highest level first i.e. return
|
||||
// < 0 if x is less than y
|
||||
// > 0 if x is greater than y
|
||||
// 0 if x and y are equal
|
||||
//
|
||||
selectedEndpoints.Sort((y, x) => x.SecurityLevel - y.SecurityLevel);
|
||||
|
||||
foreach (EndpointDescription endpoint in selectedEndpoints)
|
||||
{
|
||||
ConfiguredEndpoint configuredEndpoint = new ConfiguredEndpoint(
|
||||
endpoint.Server, EndpointConfiguration.Create(Module.Configuration));
|
||||
configuredEndpoint.Update(endpoint);
|
||||
|
||||
_session = await Session.Create(
|
||||
Module.Configuration,
|
||||
configuredEndpoint,
|
||||
true,
|
||||
false,
|
||||
Module.Configuration.ApplicationName,
|
||||
60000,
|
||||
new UserIdentity(new AnonymousIdentityToken()),
|
||||
null);
|
||||
|
||||
if (_session != null)
|
||||
{
|
||||
var subscription = new Subscription(_session.DefaultSubscription);
|
||||
subscription.PublishingInterval = PublishingInterval;
|
||||
|
||||
// TODO: Make other subscription settings configurable...
|
||||
|
||||
subscription.AddItems(MonitoredItems);
|
||||
_session.AddSubscription(subscription);
|
||||
subscription.Create();
|
||||
|
||||
Console.WriteLine($"Opc.Ua.Client.SampleModule: Created session with updated endpoint {configuredEndpoint.EndpointUrl} from server!");
|
||||
_session.KeepAlive += new KeepAliveEventHandler(StandardClient_KeepAlive);
|
||||
|
||||
// Done
|
||||
return;
|
||||
}
|
||||
|
||||
Console.WriteLine($"Opc.Ua.Client.SampleModule: WARNING Could not create session to endpoint {endpoint.ToString()}...");
|
||||
// ... try another endpoint until we do not have any more...
|
||||
}
|
||||
|
||||
throw new Exception("Failed to find acceptable endpoint to connect to.");
|
||||
}
|
||||
|
||||
private void MonitoredItem_Notification(MonitoredItem monitoredItem, MonitoredItemNotificationEventArgs e)
|
||||
{
|
||||
try
|
||||
{
|
||||
MonitoredItemNotification notification = e.NotificationValue as MonitoredItemNotification;
|
||||
if (notification == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
DataValue value = notification.Value as DataValue;
|
||||
if (value == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
using (var encoder = new JsonEncoder(monitoredItem.Subscription.Session.MessageContext, false))
|
||||
{
|
||||
string applicationURI = monitoredItem.Subscription.Session.Endpoint.Server.ApplicationUri;
|
||||
encoder.WriteString("ApplicationUri", applicationURI);
|
||||
encoder.WriteString("DisplayName", monitoredItem.DisplayName);
|
||||
encoder.WriteNodeId("MonitoredItem", monitoredItem.ResolvedNodeId);
|
||||
// suppress output of server timestamp in json by setting it to minvalue
|
||||
value.ServerTimestamp = DateTime.MinValue;
|
||||
encoder.WriteDataValue("Value", value);
|
||||
|
||||
string json = encoder.CloseAndReturnText();
|
||||
|
||||
var properties = new Dictionary<string, string>();
|
||||
properties.Add("content-type", "application/opcua+uajson");
|
||||
properties.Add("deviceName", Id);
|
||||
|
||||
if (SharedAccessKey != null)
|
||||
{
|
||||
properties.Add("source", "mapping");
|
||||
properties.Add("deviceKey", SharedAccessKey);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
Module.Publish(new Message(json, properties));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine("Opc.Ua.Client.SampleModule: Failed to publish message, dropping...");
|
||||
Console.WriteLine(ex.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
Console.WriteLine("Opc.Ua.Client.SampleModule: Error processing monitored item notification.");
|
||||
Console.WriteLine(exception.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
private void StandardClient_KeepAlive(Session sender, KeepAliveEventArgs e)
|
||||
{
|
||||
if (e != null && sender != null)
|
||||
{
|
||||
if (!ServiceResult.IsGood(e.Status))
|
||||
{
|
||||
Console.WriteLine($"Opc.Ua.Client.SampleModule: Server {sender.ConfiguredEndpoint.EndpointUrl} Status NOT good: {e.Status} {sender.OutstandingRequestCount}/{sender.DefunctRequestCount}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private EndpointDescriptionCollection DiscoverEndpoints(ApplicationConfiguration config, Uri discoveryUrl, int timeout)
|
||||
{
|
||||
EndpointConfiguration configuration = EndpointConfiguration.Create(config);
|
||||
configuration.OperationTimeout = timeout;
|
||||
|
||||
using (DiscoveryClient client = DiscoveryClient.Create(discoveryUrl, EndpointConfiguration.Create(config)))
|
||||
{
|
||||
try
|
||||
{
|
||||
EndpointDescriptionCollection endpoints = client.GetEndpoints(null);
|
||||
ReplaceLocalHostWithRemoteHost(endpoints, discoveryUrl);
|
||||
return endpoints;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Console.WriteLine($"Opc.Ua.Client.SampleModule: Could not fetch endpoints from url: {discoveryUrl}");
|
||||
Console.WriteLine($"Opc.Ua.Client.SampleModule: Reason = {e.Message}");
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void ReplaceLocalHostWithRemoteHost(EndpointDescriptionCollection endpoints, Uri discoveryUrl)
|
||||
{
|
||||
foreach (EndpointDescription endpoint in endpoints)
|
||||
{
|
||||
endpoint.EndpointUrl = Utils.ReplaceLocalhost(endpoint.EndpointUrl, discoveryUrl.DnsSafeHost);
|
||||
StringCollection updatedDiscoveryUrls = new StringCollection();
|
||||
|
||||
foreach (string url in endpoint.Server.DiscoveryUrls)
|
||||
{
|
||||
updatedDiscoveryUrls.Add(Utils.ReplaceLocalhost(url, discoveryUrl.DnsSafeHost));
|
||||
}
|
||||
|
||||
endpoint.Server.DiscoveryUrls = updatedDiscoveryUrls;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Dispose
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Dispose implementation
|
||||
/// </summary>
|
||||
/// <param name="disposing"></param>
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
if (!_disposedValue)
|
||||
{
|
||||
lock (this)
|
||||
{
|
||||
if (!_disposedValue)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
if (_session != null)
|
||||
{
|
||||
_session.Dispose();
|
||||
_session = null;
|
||||
}
|
||||
}
|
||||
|
||||
_disposedValue = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Session _session;
|
||||
private bool _disposedValue = false; // To detect redundant calls
|
||||
}
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup>
|
||||
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">14.0</VisualStudioVersion>
|
||||
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VSToolsPath)\DotNet\Microsoft.DotNet.Props" Condition="'$(VSToolsPath)' != ''" />
|
||||
<PropertyGroup Label="Globals">
|
||||
<ProjectGuid>{43311AFB-D7C4-4E5A-B1DE-855407F90D1B}</ProjectGuid>
|
||||
<RootNamespace>Opc.Ua.Client.Module</RootNamespace>
|
||||
<BaseIntermediateOutputPath Condition="'$(BaseIntermediateOutputPath)'=='' ">.\obj</BaseIntermediateOutputPath>
|
||||
<OutputPath Condition="'$(OutputPath)'=='' ">.\bin\</OutputPath>
|
||||
<TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<SchemaVersion>2.0</SchemaVersion>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VSToolsPath)\DotNet\Microsoft.DotNet.targets" Condition="'$(VSToolsPath)' != ''" />
|
||||
</Project>
|
|
@ -16,3 +16,10 @@ using System.Runtime.InteropServices;
|
|||
|
||||
// The following GUID is for the ID of the typelib if this project is exposed to COM
|
||||
[assembly: Guid("6C6DEC77-2861-4150-A307-74F733186A27")]
|
||||
|
||||
[assembly: AssemblyVersion("0.1.15.0")]
|
||||
|
||||
#if RELEASE_DELAY_SIGN
|
||||
[assembly: AssemblyDelaySign(true)]
|
||||
[assembly: AssemblyKeyFile("35MSSharedLib1024.snk")]
|
||||
#endif
|
|
@ -0,0 +1,61 @@
|
|||
{
|
||||
"version": "0.1.6",
|
||||
"name": "Opc.Ua.Client.Module",
|
||||
"title": "Opc Ua Publisher Module for Azure IoT Field Gateway",
|
||||
"description": "Managed OPC UA Publisher module for the Azure IoT Field Gateway",
|
||||
"authors": [ "microsoft" ],
|
||||
"buildOptions": {
|
||||
"xmlDoc": false,
|
||||
"allowUnsafe": true,
|
||||
"warningsAsErrors": true,
|
||||
"nowarn": [ "1591", "1734" ],
|
||||
"define": [ "TRACE" ]
|
||||
},
|
||||
"packOptions": {
|
||||
"tags": [ "Azure", "IoT", ".NET", "OPC UA", "Gateway" ],
|
||||
"projectUrl": "https://github.com/Azure/iot-gateway-opc-ua",
|
||||
"licenseUrl": "https://raw.githubusercontent.com/Azure/iot-gateway-opc-ua/master/license.txt",
|
||||
"releaseNotes": "https://github.com/Azure/iot-gateway-opc-ua/releases",
|
||||
"requireLicenseAcceptance": true,
|
||||
"repository": {
|
||||
"url": "https://github.com/Azure/iot-gateway-opc-ua"
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"OPCFoundation.NetStandard.Opc.Ua.Core": "0.1.6",
|
||||
"OPCFoundation.NetStandard.Opc.Ua.SDK": "0.1.6",
|
||||
"Portable.BouncyCastle": "1.8.1.2",
|
||||
"NETStandard.Library": "1.6.1",
|
||||
"Newtonsoft.Json": "9.0.1"
|
||||
},
|
||||
"frameworks": {
|
||||
"net46": {
|
||||
"dependencies": {
|
||||
"Azure.IoT.Gateway.SDK.Net": "2017.1.13.2"
|
||||
}
|
||||
},
|
||||
"netstandard1.6": {
|
||||
"dependencies": {
|
||||
"Microsoft.Azure.IoT.Gateway": "1.0.0-*",
|
||||
"System.Runtime.Serialization.Json": "4.3.0",
|
||||
"system.xml.xpath.xmldocument": "4.3.0",
|
||||
"System.Xml.XmlDocument": "4.3.0",
|
||||
"System.Reflection.TypeExtensions": "4.3.0",
|
||||
"System.Private.ServiceModel": "4.1.0"
|
||||
}
|
||||
}
|
||||
},
|
||||
"configurations": {
|
||||
"Signed": {
|
||||
"buildOptions": {
|
||||
"define": [ "RELEASE_DELAY_SIGN" ],
|
||||
"optimize": true
|
||||
}
|
||||
},
|
||||
"Release": {
|
||||
"buildOptions": {
|
||||
"optimize": true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Загрузка…
Ссылка в новой задаче