convertersystems / opc-ua-client

Visualize and control your enterprise using OPC Unified Architecture (OPC UA) and Visual Studio.
MIT License
397 stars 115 forks source link

Doubt #196

Closed TDawa17 closed 3 years ago

TDawa17 commented 3 years ago

Greetings from Bhutan, I am currently working on some project to pull data from opc server to winForm(Visual Studio-C#) client. I have used WorkStation.Ua to build the client in winForm. I have successfully connected with the server and was able to pull the data on message box, however, I couldn't display the data on label. Could you kindly help?

Hopping for positive reply

awcullen commented 3 years ago

Could you share some of your code to display data on a label?

TDawa17 commented 3 years ago

Hi Andrew Cullen, Kindly find the attached code file below. I am trying to get power transmission data on the opc client. I was able to create and access data on an opc client, however, I was not able to display it on a text box or label. One more question sir, when we access the data from the opc server, it gives us in the form of qmt(quality, magnitude and time) separated with semicolon(;). While I was exploring the function, I found out that I can print server time and status separately but while printing the value (magnitude) it gave me a status hex-code and time as well. Is there any function that I can use to print values(magnitude) only?

Hoping to hear from you soon and good day :)

On Tue, Apr 6, 2021 at 12:07 AM Andrew Cullen @.***> wrote:

Could you share some of your code to display data on a label?

— You are receiving this because you authored the thread. Reply to this email directly, view it on GitHub https://github.com/convertersystems/opc-ua-client/issues/196#issuecomment-813546930, or unsubscribe https://github.com/notifications/unsubscribe-auth/ARAEBQOR3MVYDOQBBCHYXC3THH35HANCNFSM42EZ2LVQ .

-- Thanking You,

Sincerely Tashi Dawa 150725A

using System; using System.IO; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Reactive.Linq; using System.Text; using System.Threading.Tasks; using System.Windows.Forms; using Workstation.ServiceModel.Ua; using Workstation.ServiceModel.Ua.Channels;

namespace Text_Box { public partial class Form1 : Form { public Form1() { InitializeComponent();

        try
        {
            Task.Run(TestAsync).GetAwaiter().GetResult();

        }
        catch (Exception )
        {
            lbl1.Text = "Could Not Connect!!!";

        }
    }

    private async Task TestAsync()
    {

        //var discoveryUrl = $"opc.tcp://LAPTOP-KLGO334G:49320"; // UaCppServer - see  http://www.unified-automation.com/
        //var discoveryUrl = $"opc.tcp://127.0.0.1:49320"; // Workstation.RobotServer
        var discoveryUrl = $"opc.tcp://support.wondershare.net:53530/OPCUA/SimulationServer";

        //this is must to create
        var appDescription = new ApplicationDescription()
        {
            ApplicationName = "OPC_Client",
            ApplicationUri = $"urn:{System.Net.Dns.GetHostName()}:OPC_Client",
            ApplicationType = ApplicationType.Client,
        };

        var channel = new UaTcpSessionChannel(
            appDescription,
            null, //you can have certification for security
            new AnonymousIdentity(),
            discoveryUrl);

        await channel.OpenAsync();

        var subscriptionRequest = new CreateSubscriptionRequest
        {
        RequestedPublishingInterval = 1000,
        RequestedMaxKeepAliveCount = 10,
        RequestedLifetimeCount = 30,
        PublishingEnabled = true
        };
        var subscriptionResponse = await channel.CreateSubscriptionAsync(subscriptionRequest);
        var id = subscriptionResponse.SubscriptionId;

        var itemsToCreate = new MonitoredItemCreateRequest[]
        {
        //new MonitoredItemCreateRequest { ItemToMonitor = new ReadValueId { NodeId = NodeId.Parse("ns=2;s=SCADA.CHM.CHM_MCONTROL/GGIO2$MX$AnIn17$mag$f"), AttributeId = AttributeIds.Value }, MonitoringMode = MonitoringMode.Reporting, RequestedParameters = new MonitoringParameters { ClientHandle = 75, SamplingInterval = -1, QueueSize = 0, DiscardOldest = true } },
        //new MonitoredItemCreateRequest { ItemToMonitor = new ReadValueId { NodeId = NodeId.Parse("ns=2;s=WMS.Sink_Node.FM_1"), AttributeId = AttributeIds.Value }, MonitoringMode = MonitoringMode.Reporting, RequestedParameters = new MonitoringParameters { ClientHandle = 88, SamplingInterval = -1, QueueSize = 0, DiscardOldest = true } }
        new MonitoredItemCreateRequest { ItemToMonitor = new ReadValueId { NodeId = NodeId.Parse("ns=3;i=1008"), AttributeId = AttributeIds.Value }, MonitoringMode = MonitoringMode.Reporting, RequestedParameters = new MonitoringParameters { ClientHandle = 88, SamplingInterval = -1, QueueSize = 0, DiscardOldest = true} },
        //new MonitoredItemCreateRequest { ItemToMonitor = new ReadValueId { NodeId = NodeId.Parse("ns=3;i=1009"), AttributeId = AttributeIds.Value }, MonitoringMode = MonitoringMode.Reporting, RequestedParameters = new MonitoringParameters { ClientHandle = 20, SamplingInterval = -1, QueueSize = 0, DiscardOldest = true } }
                /*-----------------------------------------------------CHM DATA---------------------------------------------------------------------------*/
        };
        var itemsRequest = new CreateMonitoredItemsRequest
        {
            SubscriptionId = id,
            ItemsToCreate = itemsToCreate,
        };
        var itemsResponse = await channel.CreateMonitoredItemsAsync(itemsRequest);

        var token = channel.Where(pr => pr.SubscriptionId == id).Subscribe(pr =>
        {
            // loop thru all the data change notifications
            var dcns = pr.NotificationMessage.NotificationData.OfType<DataChangeNotification>();
            foreach (var dcn in dcns)
            {

                foreach (var min in dcn.MonitoredItems)
                {

                    //MessageBox.Show($"value: {min.Value}");
                    lbl2.Text=($"Time: {min.Value}");
                    //lbl1.Text = ($"Status: {min.Value.ServerStatus.State}");
                    //MessageBox.Show($"value: {min.Value.ServerTimestamp}");
                    //lbl1.Text=($"Value: {min.Value}");
                    //MessageBox.Show($"{min.Value}");
                }
            }
        });

    }

    private void lbl1_Click(object sender, EventArgs e)
    {

    }

    private void lbl2_Click(object sender, EventArgs e)
    {
        lbl2.AutoSize = true;

    }

    private void txtBox1_TextChanged(object sender, EventArgs e)
    {

    }
}

}

awcullen commented 3 years ago

Hi, Thank you for your code. I changed three things and the label is working for me :).

First, You should create a certificate store, or be sure to set SecurityPolicyUris.None when you construct the channel. Second, Add '.ObserveOn(this)' when subscribing to be sure the callback will be on the correct thread to modify the label. Third, Use the ClientHandle to identify the item that changed. You can then use GetValueOrDefault<T>()

private async Task TestAsync()
{
    var discoveryUrl = $"opc.tcp://localhost:48010";

    //this is must to create 
    var appDescription = new ApplicationDescription()
    {
        ApplicationName = "OPC_Client",
        ApplicationUri = $"urn:{System.Net.Dns.GetHostName()}:OPC_Client",
        ApplicationType = ApplicationType.Client,
    };

    var channel = new UaTcpSessionChannel(
        appDescription,
        null, //certificateStore, 
        new AnonymousIdentity(),
        discoveryUrl,
        SecurityPolicyUris.None);  // Add this to be sure

    await channel.OpenAsync();

    var subscriptionRequest = new CreateSubscriptionRequest
    {
        RequestedPublishingInterval = 1000,
        RequestedMaxKeepAliveCount = 10,
        RequestedLifetimeCount = 30,
        PublishingEnabled = true
    };
    var subscriptionResponse = await channel.CreateSubscriptionAsync(subscriptionRequest);
    var id = subscriptionResponse.SubscriptionId;

    var itemsToCreate = new MonitoredItemCreateRequest[]
    {
        new MonitoredItemCreateRequest { ItemToMonitor = new ReadValueId { NodeId = NodeId.Parse("ns=0;i=2258"), AttributeId = AttributeIds.Value }, MonitoringMode = MonitoringMode.Reporting, RequestedParameters = new MonitoringParameters { ClientHandle = 88, SamplingInterval = -1, QueueSize = 0, DiscardOldest = true} },
    };
    var itemsRequest = new CreateMonitoredItemsRequest
    {
        SubscriptionId = id,
        ItemsToCreate = itemsToCreate,
    };
    var itemsResponse = await channel.CreateMonitoredItemsAsync(itemsRequest);
    // better check if the server found all the nodeId's in the request
    foreach (var item in itemsResponse.Results)
    {
        if (StatusCode.IsBad(item.StatusCode))
        {
            throw new ServiceResultException(item.StatusCode);
        }
    }

    // add  '.ObserveOn(this)' when you wish the callback to be on the correct thread to modify a control
    var token = channel.Where(pr => pr.SubscriptionId == id).ObserveOn(this).Subscribe(pr =>
    {
        var dcns = pr.NotificationMessage.NotificationData.OfType<DataChangeNotification>();
        foreach (var dcn in dcns)
        {

            foreach (var min in dcn.MonitoredItems)
            {
                switch (min.ClientHandle)
                {
                    case 88:  // the client handle identifies item that changed.
                lbl1.Text = ($"Time: {min.Value.GetValueOrDefault<DateTime>()}");
                        break;
                }
            }
        }
    });

}
TDawa17 commented 3 years ago

Hi Andrew, Thank You so much for the prompt response. With this lbl1.Text = ($"Value: {min.Value.GetValueOrDefault()}"); line of code and GetValueOr Default I can get ServerState and DateTime on label as stand alone. Is it possible to get just value on label without showing the server state and time? When I used lbl1.Text = ($"Value: {min.Value.GetValueOrDefault()}"); i didn't get any data on the label.

Hoping for a positive response. Thank you and Good Day :)

On Wed, Apr 7, 2021 at 7:25 AM Andrew Cullen @.***> wrote:

Hi, Thank you for your code. I changed three things and the label is working for me :).

First, You should create a certificate store, or be sure to set SecurityPolicyUris.None when you construct the channel. Second, Add '.ObserveOn(this)' when subscribing to be sure the callback will be on the correct thread to modify the label. Third, Use the ClientHandle to identify the item that changed. You can then use GetValueOrDefault()

private async Task TestAsync() { var discoveryUrl = $"opc.tcp://localhost:48010";

//this is must to create
var appDescription = new ApplicationDescription()
{
    ApplicationName = "OPC_Client",
    ApplicationUri = $"urn:{System.Net.Dns.GetHostName()}:OPC_Client",
    ApplicationType = ApplicationType.Client,
};

var channel = new UaTcpSessionChannel(
    appDescription,
    null, //certificateStore,
    new AnonymousIdentity(),
    discoveryUrl,
    SecurityPolicyUris.None);  // Add this to be sure

await channel.OpenAsync();

var subscriptionRequest = new CreateSubscriptionRequest
{
    RequestedPublishingInterval = 1000,
    RequestedMaxKeepAliveCount = 10,
    RequestedLifetimeCount = 30,
    PublishingEnabled = true
};
var subscriptionResponse = await channel.CreateSubscriptionAsync(subscriptionRequest);
var id = subscriptionResponse.SubscriptionId;

var itemsToCreate = new MonitoredItemCreateRequest[]
{
    new MonitoredItemCreateRequest { ItemToMonitor = new ReadValueId { NodeId = NodeId.Parse("ns=0;i=2258"), AttributeId = AttributeIds.Value }, MonitoringMode = MonitoringMode.Reporting, RequestedParameters = new MonitoringParameters { ClientHandle = 88, SamplingInterval = -1, QueueSize = 0, DiscardOldest = true} },
};
var itemsRequest = new CreateMonitoredItemsRequest
{
    SubscriptionId = id,
    ItemsToCreate = itemsToCreate,
};
var itemsResponse = await channel.CreateMonitoredItemsAsync(itemsRequest);

// add  '.ObserveOn(this)' when you wish the callback to be on the correct thread to modify a control
var token = channel.Where(pr => pr.SubscriptionId == id).ObserveOn(this).Subscribe(pr =>
{
    var dcns = pr.NotificationMessage.NotificationData.OfType<DataChangeNotification>();
    foreach (var dcn in dcns)
    {

        foreach (var min in dcn.MonitoredItems)
        {
            switch (min.ClientHandle)
            {
                case 88:  // the client handle identifies item that changed.
                    lbl1.Text = ($"Time: {min.Value.GetValueOrDefault<DateTime>()}");
                    break;
            }
        }
    }
});

}

— You are receiving this because you authored the thread. Reply to this email directly, view it on GitHub https://github.com/convertersystems/opc-ua-client/issues/196#issuecomment-814534695, or unsubscribe https://github.com/notifications/unsubscribe-auth/ARAEBQNWAX2STDF7KC5F7RLTHOYADANCNFSM42EZ2LVQ .

-- Thanking You,

Sincerely Tashi Dawa 150725A

ismdiego commented 3 years ago

Hi @TDawa17,

I hope I can help here. By value do you mean the numeric value of the ServerState (it is an enum)? If so, you can just cast the value to long:

lbl1.Text = $"Value: {(long)min.Value.GetValueOrDefault<ServerState>()}";

Is this enough for your needs? Or were you asking for something different?

This did not work...:

lbl1.Text = ($"Value: {min.Value.GetValueOrDefault<DataValue>()}");

...because the GetValueOrDefault is asking the min.Value (which is a DataValue, just a holder of a value with timestamps) to cast the internal value to another DataValue, which is not.

To be clear, when you create a MonitoredItemRequest you know the type/s of the variable/s in anticipation, and you can use the GetValueOrDefault as a convenience method to get it. For instance, if you are subscribing to a int32 value, you declare a ReadValueId with the NodeId, etc... and then when you read it you can do min.Value.GetValueOrDefault<Int32>() to get the int32 value.

In the example @awcullen wrote, the NodeId "ns=0;i=2258" is of type ServerState (which is a enum). And so the GetValueOrDefault<ServerState>() just return that enum. Nothing else. No timestamp, etc...

I think that maybe you are referring to min.Value which has all the timestamps you are talking about. Can this is be the case?

lbl1.Text = ($"Value: {min.Value}");

TDawa17 commented 3 years ago

Hi Andrew, Thank you for the prompt reply. It worked :). Thank you for the info. Good Day :)

On Thu, Apr 8, 2021 at 12:10 AM ismdiego @.***> wrote:

Hi @TDawa17 https://github.com/TDawa17,

I hope I can help here. By value do you mean the numeric value of the ServerState (it is an enum)? If so, you can just cast the value to long:

lbl1.Text = $"Value: {(long)min.Value.GetValueOrDefault()}";

Is this enough for your needs? Or were you asking for something different?

This did not work...:

lbl1.Text = ($"Value: {min.Value.GetValueOrDefault()}");

...because the GetValueOrDefault is asking the min.Value (which is a DataValue, just a holder of a value with timestamps) to cast the internal value to another DataValue, which is not.

To be clear, when you create a MonitoredItemRequest you know the type/s of the variable/s in anticipation, and you can use the GetValueOrDefault as a convenience method to get it. For instance, if you are subscribing to a int32 value, you declare a ReadValueId with the NodeId, etc... and then when you read it you can do min.Value.GetValueOrDefault() to get the int32 value.

In the example @awcullen https://github.com/awcullen wrote, the NodeId "ns=0;i=2258" is of type ServerState (which is a enum). And so the GetValueOrDefault() just return that enum. Nothing else. No timestamp, etc...

I think that maybe you are referring to min.Value which has all the timestamps you are talking about. Can this is be the case?

lbl1.Text = ($"Value: {min.Value}");

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/convertersystems/opc-ua-client/issues/196#issuecomment-815118534, or unsubscribe https://github.com/notifications/unsubscribe-auth/ARAEBQKDPCLEFUSLIQVQWADTHSNY3ANCNFSM42EZ2LVQ .

-- Thanking You,

Sincerely Tashi Dawa 150725A

TDawa17 commented 3 years ago

Hi Andrew, Thank you for the explanation. It worked :) Is there any function that I have to use to send this real time data to database such as postgresql simultaneously?

Wish you a great day.

On Thu, Apr 8, 2021 at 12:10 AM ismdiego @.***> wrote:

Hi @TDawa17 https://github.com/TDawa17,

I hope I can help here. By value do you mean the numeric value of the ServerState (it is an enum)? If so, you can just cast the value to long:

lbl1.Text = $"Value: {(long)min.Value.GetValueOrDefault()}";

Is this enough for your needs? Or were you asking for something different?

This did not work...:

lbl1.Text = ($"Value: {min.Value.GetValueOrDefault()}");

...because the GetValueOrDefault is asking the min.Value (which is a DataValue, just a holder of a value with timestamps) to cast the internal value to another DataValue, which is not.

To be clear, when you create a MonitoredItemRequest you know the type/s of the variable/s in anticipation, and you can use the GetValueOrDefault as a convenience method to get it. For instance, if you are subscribing to a int32 value, you declare a ReadValueId with the NodeId, etc... and then when you read it you can do min.Value.GetValueOrDefault() to get the int32 value.

In the example @awcullen https://github.com/awcullen wrote, the NodeId "ns=0;i=2258" is of type ServerState (which is a enum). And so the GetValueOrDefault() just return that enum. Nothing else. No timestamp, etc...

I think that maybe you are referring to min.Value which has all the timestamps you are talking about. Can this is be the case?

lbl1.Text = ($"Value: {min.Value}");

— You are receiving this because you were mentioned.

Reply to this email directly, view it on GitHub https://github.com/convertersystems/opc-ua-client/issues/196#issuecomment-815118534, or unsubscribe https://github.com/notifications/unsubscribe-auth/ARAEBQKDPCLEFUSLIQVQWADTHSNY3ANCNFSM42EZ2LVQ .

-- Thanking You,

Sincerely Tashi Dawa 150725A

ismdiego commented 3 years ago

Hi @TDawa17,

Hi Andrew, Thank you for the explanation. It worked :) Is there any function that I have to use to send this real time data to database such as postgresql simultaneously? Wish you a great day.

This question is totally out of scope for this library. You can better follow steps in this tutorial: https://zetcode.com/csharp/postgresql/

Once you get the value (in this case, the min.Value.GetValueOrDefault<T>()) you can do whatever you want with the data. Display in a label (like the sample below), insert in a database (like the samples in the tutorial), etc.

You can find more details about the Npgsql library at: https://www.npgsql.org/doc/index.html

Also, you can ask there for help with this or, as suggested, search in Stack Overflow questions with the npgsql tag: https://www.npgsql.org/#getting-help