Merge pull request #15 from marcschier/master

opc-ua module v2 for .net standard loader.
This commit is contained in:
Marc Schier 2017-02-21 09:42:08 +00:00 коммит произвёл GitHub
Родитель e2650f23ab 763a6145ee
Коммит 507e0fe94c
24 изменённых файлов: 1345 добавлений и 1210 удалений

239
.gitignore поставляемый
Просмотреть файл

@ -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/**

Двоичные данные
.nuget/Microsoft.Azure.IoT.Gateway.1.0.0.nupkg Normal file

Двоичный файл не отображается.

Двоичные данные
.nuget/Microsoft.Azure.IoT.Gateway.1.0.0.symbols.nupkg Normal file

Двоичный файл не отображается.

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;
}
}
}

16
NuGet.Config Normal file
Просмотреть файл

@ -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>

Просмотреть файл

@ -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;
}

279
bld/build.cmd Normal file
Просмотреть файл

@ -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

220
bld/build.sh Normal file
Просмотреть файл

@ -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 $?

10
bld/publish/dummy.cs Normal file
Просмотреть файл

@ -0,0 +1,10 @@
namespace Opc.Ua.Client
{
class Program
{
static void Main(string[] args)
{
var module = new SampleModule();
}
}
}

18
bld/publish/project.json Normal file
Просмотреть файл

@ -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"
}
]
}

5
global.json Normal file
Просмотреть файл

@ -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"
}
]
}

Двоичные данные
src/Opc.Ua.Client.Module/35MSSharedLib1024.snk Normal file

Двоичный файл не отображается.

Просмотреть файл

@ -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
}
}
}
}