OPCFoundation / UA-.NETStandard

OPC Unified Architecture .NET Standard
Other
1.94k stars 942 forks source link

Does this UA .Net standard support Unity's IL2CPP Scripting Backend? HELP !!!! #760

Closed zxzkf1992 closed 5 years ago

zxzkf1992 commented 5 years ago

I am developing a UWP application that allows HoloLens to connect to the OPC UA server. So I want to confirm, is the current version of UA .Net Standard now supporting Unity IL2CPP scripting backend, or can it only support .Net/Mono scripting backend?

I hope someone can reply to me as soon as possible. This question is very important to me, thanks a lot.

MD-V commented 5 years ago

@zxzkf1992 I don't think that anyone tried this before but as IL2CPP can utilize .NetStandard 2.0 it should work.

zxzkf1992 commented 5 years ago

@MD-V thanks for your reply. I have tried it yesterday. It works in Unity Editor, but not in HoloLens, there will give more than one error.

MD-V commented 5 years ago

@zxzkf1992 Can you give more details about the error?

zxzkf1992 commented 5 years ago

@MD-V Here is the error message given by HoloLens.

System.AggregateException: One or more errors occurred. --> System.UnauthorizedAccessException: Failed gettingthe path of a special folder: Access Denied. at System.Environment.GetFolderPath(System.Environment+SpecialFolder folder) at Opc.Ua.Utils.ReplaceSpecialFolderNames(System.Stringinput)

MD-V commented 5 years ago

@zxzkf1992

Please check if the directories you configured for CertificateManagement in the ApplicationConfiguration are existing (and accessible) on the HoloLens and edit your ApplicationConfiguration to point to paths where your program has access to...

The properties you need to check are: ApplicationConfiguration.SecurityConfiguration.ApplicationCertificate.StorePath ApplicationConfiguration.SecurityConfiguration.TrustedPeerCertificates.StorePath ApplicationConfiguration.SecurityConfiguration.TrustedIssuerCertificates.StorePath ApplicationConfiguration.SecurityConfiguration.RejectedCertificateStore.StorePath

zxzkf1992 commented 5 years ago

@MD-V This program is rewritten on the NetCore Console Client sample. The code for my ApplicationConfiguration is as follows. There is no error running on Unity. Is it possible that this directory is not in HoloLens?

    var config = new ApplicationConfiguration()
    {
        ApplicationName = "OpcUA-Test",
        ApplicationUri = Utils.Format(@"urn:{0}:OpcUA-Test", Dns.GetHostName()),
        ApplicationType = ApplicationType.Client,
        SecurityConfiguration = new SecurityConfiguration
        {
            ApplicationCertificate = new CertificateIdentifier { StoreType = @"Directory", StorePath = @"%CommonApplicationData%\OPC Foundation\CertificateStores\MachineDefault", SubjectName = Utils.Format(@"CN={0}, DC={1}", "OpcUA-Test", Dns.GetHostName()) },
            TrustedIssuerCertificates = new CertificateTrustList { StoreType = @"Directory", StorePath = @"%CommonApplicationData%\OPC Foundation\CertificateStores\UA Certificate Authorities" },
            TrustedPeerCertificates = new CertificateTrustList { StoreType = @"Directory", StorePath = @"%CommonApplicationData%\OPC Foundation\CertificateStores\UA Applications" },
            RejectedCertificateStore = new CertificateTrustList { StoreType = @"Directory", StorePath = @"%CommonApplicationData%\OPC Foundation\CertificateStores\RejectedCertificates" },
            AutoAcceptUntrustedCertificates = true,
            AddAppCertToTrustedStore = true
        },
        TransportConfigurations = new TransportConfigurationCollection(),
        TransportQuotas = new TransportQuotas { OperationTimeout = 15000 },
        ClientConfiguration = new ClientConfiguration { DefaultSessionTimeout = 60000 },
        TraceConfiguration = new TraceConfiguration()
    };
MD-V commented 5 years ago

It works on Unity because you are running on standard windows and %CommonApplicationData% exists. I think you cannot use %CommonApplicationData% on HoloLens. You'll have to change this paths!

zxzkf1992 commented 5 years ago

@MD-V I´m very new for the OPC UA. I don't understand the settings of ApplicationConfiguration. I have a problem now, this applicationConfiguraion I have to set up in the Client, or has been set up in the OPC UA library, I can call directly? Because in the NetCoreClient sample i don't see the code to set the ApplicaitonConfiguration.

Thank you very much for your patience.

MD-V commented 5 years ago

You have to set the config up in the client. You can do it two ways: programmaticaly or load from an xml (see)

After loading the configuration you can adjust its values or you edit the xml file directly.

EDIT: A little example (taken from .NetCoreConsoleClient, paths are just examples you will have to choose matching paths for the HoloLens) ApplicationConfiguration config = await application.LoadApplicationConfiguration(false); config.SecurityConfiguration.ApplicationCertificate.StoreLocation = "./OPC Foundation/pki/own"; config.SecurityConfiguration.TrustedIssuerCertificates.StoreLocation = "./OPC Foundation/pki/issue"; config.SecurityConfiguration.TrustedPeerCertificates.StoreLocation = "./OPC Foundation/pki/trusted"; config.SecurityConfiguration.RejectedCertificateStore.StoreLocation = "./OPC Foundation/pki/rejected";

zxzkf1992 commented 5 years ago

@MD-V Hello, I modified the code yesterday, set the path to the local folder of HoloLens, and now fix the problem of ApplicaitonConfiguration. But now the following error has occurred, is it because OPC UA still only supports Mono, but not IL2CPP?

System.TypeInitializationException: The type initializer for `Opc.UaServiceMessageContext' threw an exception.--->System.TypeInitializationException: The type initializer for ´Opc.Ua.EncodeableFactory' threw an exception.---> System.MissingMethodException: Default constructor not found for type Opc.Ua.Argument at system.RuntimeType.CreateInsatanceMono(System.Boolean nonPublic).

MD-V commented 5 years ago

@zxzkf1992 Are you running with Release or Debug build on the HoloLens?

zxzkf1992 commented 5 years ago

@MD-V yes, runing with Release build on the HoloLens

MD-V commented 5 years ago

@zxzkf1992 Can you try running it with Debug? It seems that IL2CPP strips the ctor for Opc.Ua.Argument because its only used via Reflection...

zxzkf1992 commented 5 years ago

@MD-V I just tried debug build on HoloLens and it has the same error as release build.

MD-V commented 5 years ago

@zxzkf1992 Okay, i'm currently out of ideas. Is it possible for you to upload your project to github? We have a HoloLens so we could try to get it working.

zxzkf1992 commented 5 years ago

@MD-V Ok, I can upload this project to GitHub, or can I package the project directly into a ZIP file and send it to you via email? You also have HoloLens, which sounds great. Have you tried using the OPC UA .Net standard library on HoloLens to connect to the OPC UA server?

MD-V commented 5 years ago

@zxzkf1992 I don't like to post my mail address publicly here. How can I contact you?

zxzkf1992 commented 5 years ago

@MD-V Sorry, I didn't think about it. I just created a new email address, you can send an email to this address awhujx25806@chacuo.net, so I will send the project to you.

zxzkf1992 commented 5 years ago

@MD-V Hello, can you send an email to this address? I haven't received it until now. If you don't want to send an email, I uploaded my project to Google drive. I can share this link for you.

MD-V commented 5 years ago

@zxzkf1992 I have sent one yesterday evening and another one just now. Can you please check again?

MD-V commented 5 years ago

@zxzkf1992

Hello,

the problem is indeed the compilation with IL2CPP.

IL2CPP only emits code to C++ that is actually called (see here ).

The EncodableFactory of OPC UA uses Reflection to add all system types (see here and here).

So the default constructors for e.g. Opc.UA.Argument are never called explicitly and thereby not emitted by IL2CPP.

A workaround could be that you call all the default constructors of all IEncodable datatypes (there are unfortunately many!) explicitly in your code. It could look like this:

public async Task OpcUa_Connector()
    {
    // Add the default constructors of all IEncodable Types here

    var argument = new Opc.Ua.Argument();
    var enumValue = new Opc.Ua.EnumValueType();        
    ....
    ....
    ....

    //Get the HoloLens local folder address

        Class1 dll = new Class1();
        dll.CreatFolder();
        string folderPath = dll.FolderPath;

        //StorageFolder folder = ApplicationData.Current.LocalFolder;
        //string folderpath = folder.Path;
        //string filepath = folderpath + "";
        //folder.CreateFolderAsync(folder_Name, CreationCollisionOption.ReplaceExisting);

        //OpcUA Client
        //create the application configuration
        //work on HoloLens
        var config = new ApplicationConfiguration()
        {
            ApplicationName = "OpcUA-Test",

I hope this helps.

EDIT:

You can also provide a link.xml and include Opc.Ua.Core. So IL2CPP will not strip the constructors...

See here: https://docs.unity3d.com/Manual/IL2CPP-BytecodeStripping.html

link.xml

<linker>
       <assembly fullname="Opc.Ua.Core" preserve="all"/>
</linker>
Boehm92 commented 5 years ago

Hi, i have the exact same issue. @zxzkf1992 would you mind sharing your OpcUa-Client? I would be really thankfull. Kind Regards Stefan

MD-V commented 5 years ago

@Boehm92 just look at my last comment and create a link.xml file.

Boehm92 commented 5 years ago

@MD-V thanks for the fast reply. I allready created a link.xml in the assets folder. `

`

The only thing i'm not shure about is which directory i must use instead of %CommonApplicationData%.

MD-V commented 5 years ago

@Boehm92

Currently dont have a HoloLens here. Just use any path where you have write access to. e.g.: Path.GetFullPath(Windows.Storage.ApplicationData.Current.LocalFolder.Path)

zxzkf1992 commented 5 years ago

@Boehm92 sorry, i replied to you so late. Because Application running on HoloLens is real UWP. So setting the Application directory must follow the UWP rules. You can search for how to set the path of the UWP application.

Boehm92 commented 5 years ago

So, i created the link.xml file and also exchanged the directory from %CommonApplicationData% to %userprofile%. The code is working if i use the %CommonApplicationData% directory in the unity editor. But even if i change it to %userprofile% and load it to the hololens as release build it won't work. Following is the code. I'm really thankfull for any help because i try to establish a working opc ua communication via hololens for 3 weeks now and i think i nearly got it.

public void SubscripeToOpcUaServer(string OpcUaEndpoint, List NoteIdList) {

        var config = new ApplicationConfiguration()
        {
            ApplicationName = "OpcUaClientconifg",
            ApplicationUri = Utils.Format(@"urn:{0}:OpcUaClient", System.Net.Dns.GetHostName()),
            ApplicationType = ApplicationType.Client,
            SecurityConfiguration = new SecurityConfiguration
            {
                ApplicationCertificate = new CertificateIdentifier { StoreType = @"Directory", StorePath = @"%userprofile%\OPC Foundation\CertificateStores\MachineDefault", SubjectName = Utils.Format(@"CN={0}, DC={1}", "MyHomework", System.Net.Dns.GetHostName()) },
                TrustedIssuerCertificates = new CertificateTrustList { StoreType = @"Directory", StorePath = @"%userprofile%\OPC Foundation\CertificateStores\UA Certificate Authorities" },
                TrustedPeerCertificates = new CertificateTrustList { StoreType = @"Directory", StorePath = @"%userprofile%\OPC Foundation\CertificateStores\UA Applications" },
                RejectedCertificateStore = new CertificateTrustList { StoreType = @"Directory", StorePath = @"%userprofile%\OPC Foundation\CertificateStores\RejectedCertificates" },
                AutoAcceptUntrustedCertificates = true,
                AddAppCertToTrustedStore = true,
                RejectSHA1SignedCertificates = false,
                MinimumCertificateKeySize = 1024

                //ApplicationCertificate = new CertificateIdentifier { StoreType = @"Directory", StorePath = @"%CommonApplicationData%\OPC Foundation\CertificateStores\MachineDefault", SubjectName = Utils.Format(@"CN={0}, DC={1}", "MyHomework", System.Net.Dns.GetHostName()) },
                //TrustedIssuerCertificates = new CertificateTrustList { StoreType = @"Directory", StorePath = @"%CommonApplicationData%\OPC Foundation\CertificateStores\UA Certificate Authorities" },
                //TrustedPeerCertificates = new CertificateTrustList { StoreType = @"Directory", StorePath = @"%CommonApplicationData%\OPC Foundation\CertificateStores\UA Applications" },
                //RejectedCertificateStore = new CertificateTrustList { StoreType = @"Directory", StorePath = @"%CommonApplicationData%\OPC Foundation\CertificateStores\RejectedCertificates" },
                //AutoAcceptUntrustedCertificates = true,
                //AddAppCertToTrustedStore = true,
                //RejectSHA1SignedCertificates = false,
                //MinimumCertificateKeySize = 1024
            },
            TransportConfigurations = new TransportConfigurationCollection(),
            TransportQuotas = new TransportQuotas { OperationTimeout = 15000 },
            ClientConfiguration = new ClientConfiguration { DefaultSessionTimeout = 60000 },
            TraceConfiguration = new TraceConfiguration()
        };

        config.Validate(ApplicationType.Client).GetAwaiter().GetResult();
        if (config.SecurityConfiguration.AutoAcceptUntrustedCertificates)
        {
            config.CertificateValidator.CertificateValidation += (s, e) => { e.Accept = (e.Error.StatusCode == StatusCodes.BadCertificateUntrusted); };
        }

        var application = new ApplicationInstance
        {
            ApplicationName = "OpcUaClient",
            ApplicationType = ApplicationType.Client,
            ApplicationConfiguration = config
        };

        application.CheckApplicationInstanceCertificate(false, 2048).GetAwaiter().GetResult();

        var selectedEndpoint = CoreClientUtils.SelectEndpoint(OpcUaEndpoint, useSecurity: false, operationTimeout: 15000); //"opc.tcp://141.60.104.13:4840"

        session = Session.Create(config, new ConfiguredEndpoint(null, selectedEndpoint, EndpointConfiguration.Create(config)), false, "", 60000, null, null).GetAwaiter().GetResult();
        {
            ReferenceDescriptionCollection refs;
            Byte[] cp;
            session.Browse(null, null, ObjectIds.ObjectsFolder, 0u, BrowseDirection.Forward, ReferenceTypeIds.HierarchicalReferences, true, (uint)NodeClass.Variable | (uint)NodeClass.Object | (uint)NodeClass.Method, out cp, out refs);
            foreach (var rd in refs)
            {
                ReferenceDescriptionCollection nextRefs;
                byte[] nextCp;
                session.Browse(null, null, ExpandedNodeId.ToNodeId(rd.NodeId, session.NamespaceUris), 0u, BrowseDirection.Forward, ReferenceTypeIds.HierarchicalReferences, true, (uint)NodeClass.Variable | (uint)NodeClass.Object | (uint)NodeClass.Method, out nextCp, out nextRefs);
            }

            var subscription = new Subscription(session.DefaultSubscription) { PublishingInterval = 1000 };

            list = new List<MonitoredItem>();
            foreach (var item in NoteIdList)
            {
                var monitoredItem = new MonitoredItem(subscription.DefaultItem)
                {
                    StartNodeId = item
                };

                list.Add(monitoredItem);
            }
            subscription.AddItems(list);
            session.AddSubscription(subscription);
            subscription.Create();
        }
}
MD-V commented 5 years ago

@Boehm92 Please resolve the paths to full paths before using them e.g.

appCertPath = Environment.ExpandEnvironmentVariables(@"%userprofile%\OPC Foundation\CertificateStores\MachineDefault");

ApplicationCertificate = new CertificateIdentifier { StoreType = @"Directory", StorePath = appCertPath , SubjectName = Utils.Format(@"CN={0}, DC={1}", "MyHomework", System.Net.Dns.GetHostName()) },

EDIT: Please also make sure you have write access to the path you are using!

Boehm92 commented 5 years ago

@MD-V thank you very much for your support What i did so far:

  1. i used the suggested method Environment.ExpandEnvironmentVariables with the directorys from this link https://www.youtube.com/watch?v=Nk9l2knWTko&t=277s
  2. I debugged the application on the hololens and get the exception: NullReferenceException: Object reference not set to an instance of an objectOpcUaCom.Update () (at Assets/Scripts/OpcUaCom.cs:33) I get the same exception in the unity editor with these directory, but if change it to the %CommonApplicationData%-Directory the opc ua communication is working fine.

Next thing i will try is to use the approach of the link https://stackoverflow.com/questions/44014883/creating-txt-file-on-hololens from MD-V If someone else has another idea i would be realy happy about to hear it

@zxzkf1992 it would be gread if you could post your directorys

Thanks again for the support

EDIT: i used the directory _appCertPath = Path.Combine(Application.persistentDataPath, "/OPC Foundation/CertificateStores/MachineDefault"); but still the same exception. I will change my approach to restfull api. If anyone get a working approach for an hololens opc ua client please let me know

anneaftk commented 1 year ago

Hello @zxzkf1992 ! I am trying to also connect to a server with Hololens via opc ua communcation. Would it be possible to share your code where you had to make changes to work in Hololens? The email is anneafutko@gmail.com .