Traeger-GmbH / opcuanet-samples

Sample projects for C#, VB.NET and C++/CLI to guide developers using our OPC UA .NET SDKs.
https://opcua.traeger.de
MIT License
88 stars 39 forks source link

OPC-UA Multiclient on same pc #7

Closed tntsei6 closed 1 year ago

tntsei6 commented 1 year ago

Hi Everybody

I have a simple question.

I have a project where i want manage 25 client opa ua that point on 25 different server. The problem is that the clients are in the same pc and in the same project ( so 25 client with same ip address and 25 server with different ip).

Currently i have 24 server offline and 1 is online. The problem is that the client that have the server available, is not able to connect, so all is not connected. If in my project i remove the 24 offline clients, the client will be connected.

Exist a maximum opc ua client number?

i m using Opc.Ua.FX.Advanced library

Thanks

kpreisser commented 1 year ago

Hi @tntsei6, thanks for creating this issue!

In the OPC UA .NET SDK, there should not be any limit of OpcClient instances that can be used concurrently.

Can you share the code you are using to create and connect the OpcClients? Are you using the clients in different threads? What exactly happens with the client that is unable to connect but the server should be available, do you get any exception? Thanks!

tntsei6 commented 1 year ago

Hi....i think that i fixed. in my application i have created one static client and 24 not static client. Follow the code for the static client. Code work in this way: I open a connection with 1 client. if the client is not available, i open 1 thread to wait the server availability. Any Suggstions?

` private async void Inizializzazione_Client()

{

       if (Quadro_Generale != null)

            Quadro_Generale.Disconnect();

        Quadro_Generale = new OpcClient("opc.tcp://" + ip + ":4840/");

        #region Configurazioni
        Quadro_Generale.UseDynamic = true;
        Quadro_Generale.ReconnectTimeout = 100;
        Quadro_Generale.DisconnectTimeout = 100;
        Quadro_Generale.OperationTimeout = 5000;
        Quadro_Generale.SessionTimeout = 5000;

        Quadro_Generale.ApplicationName = "Supervisione";
        Quadro_Generale.SessionName = "Supervisione";
        OpcClientKeepAlive keepAlive = Quadro_Generale.KeepAlive;
        keepAlive.Interval = 500;

        Quadro_Generale.Security.UserIdentity = new OpcClientIdentity("*****", "******");
        #endregion

        #region Eventi
        Quadro_Generale.StateChanged += Check_State;

        eventi = new OpcSubscribeDataChange[] {
            new OpcSubscribeDataChange("ns=4;s=Termici", Termico_Scattato),
            new OpcSubscribeDataChange("ns=4;s=Magnetotermici", MagnetoTermico_Scattato),
            new OpcSubscribeDataChange("ns=4;s=Avarie_Inverter", Avaria_Inverter_Scattata),
        };
        #endregion

        await Task.Run(() =>
        {
            try
            {
                Quadro_Generale.Connect();
                compac = Quadro_Generale.ReadNode("ns=4;s=Compac").Value;
            }
            catch
            {
                if (T_connessione != null && T_connessione.IsAlive)
                {
                    T_connessione.Abort();
                }
                T_connessione = new Thread(Tentativo_Connessione);
                T_connessione.Start();
            }
        });
    }

    private void Tentativo_Connessione()
    {
        while (true)
        {
            if (!Connected)
            {
                try
                {
                    Quadro_Generale.Connect();
                    compac = Quadro_Generale.ReadNode("ns=4;s=Compac").Value;
                }
                catch
                {
                    Program.form.Disconnected();
                }
            }
            else
            {
                if (OnRiconnection != null) OnRiconnection(this, new EventArgs());

                if (T_connessione != null && T_connessione.IsAlive)
                {
                    T_connessione.Abort();
                }
            }
            Thread.Sleep(1000);
        }
    }`
kpreisser commented 1 year ago

Hi @tntsei6, thanks for your reply! We are glad to hear that the problem is fixed.

One thing to note that if you use Task.Run(...) to run a task in a worker thread, if the task uses blocking operations like OpcClient.Connect(), this can cause all worker threads that are available in the .NET ThreadPool to be blocked when you do this multiple times (with multiple OpcClients), and that in turn can cause Connect() to hang or fail (at least for some duration), because it needs to process responses from the server also in an available worker thread, and new worker threads are only created very slowly.

You can avoid this e.g. by creating a separate thread (new Thread(...)) instead of using new Task(...) for using blocking operations. An alternative way/work-around (at least to check if blocked worker threads is the cause of the issue) can be to increase the min worker threads count, as that allows to create new worker threads very fast when all worker threads are currently blocked:

ThreadPool.GetMaxThreads(out int maxWorkerThreads, out int maxCompletionPortThreads);
ThreadPool.GetMinThreads(out int minWorkerThreads, out int minCompletionPortThreads);
ThreadPool.SetMaxThreads(Math.Max(maxWorkerThreads, 20000), Math.Max(maxCompletionPortThreads, 2000));
ThreadPool.SetMinThreads(Math.Max(minWorkerThreads, 5000), Math.Max(minCompletionPortThreads, 200));

Thanks!