FreeOpcUa / python-opcua

LGPL Pure Python OPC-UA Client and Server
http://freeopcua.github.io/
GNU Lesser General Public License v3.0
1.35k stars 658 forks source link

Question about Python OPCUA session timeout and client. #1117

Closed Sora0224 closed 4 years ago

Sora0224 commented 4 years ago

Good day Everyone, before I start to ask question. I'm new to this forum and my knowledge in Python programming is on beginner level but I'm still trying to improve my python programming language.

So currently I've been working on a project using Python OPCUA, my python coding will act as the client to take data from the existing cloud server. I got the node address correctly and everything is working and I managed to get the data from the server, but I noticed when I run the program I got :

Requested session timeout to be 3600000ms, got 60000ms instead

How do I remove that session timeout limitation? I did some study but still I don't understand, can anyone guide me on this?

Also another question is that, how fast can the client obtain the data from server? I insert a line to check the time taken, sometimes it took 1sec sometimes it took up to 3-6sec to provide me the data.

Thanks in advance.

Sora0224 commented 4 years ago

oh ya, the reason I wanted to know how to remove the Session Timeout is because I wanted to make a real time application that run 24/7 using the data from the cloud server.

AndreasHeine commented 4 years ago

this Message just tells you that your client configuration (session timeout) is different from the settings in the server.

client.session_timeout = 600000 should do the trick!

AndreasHeine commented 4 years ago

:param timeout: Each request sent to the server expects an answer within this time. The timeout is specified in seconds.

Sora0224 commented 4 years ago

this Message just tells you that your client configuration (session timeout) is different from the settings in the server.

client.session_timeout = 600000 should do the trick!

What if I wanted it to be no timeout at all? Cause I want to keep it running the whole time, there should be a way right? Also the timeout is decided by the Master server? or by client?

AndreasHeine commented 4 years ago

It is not possible to have no timeout at all... if the client makes a request and the server does not reponde within the timeout the connection is broken or the server not running!

Also the timeout is decided by the Master server? or by client?

https://reference.opcfoundation.org/v104/Core/docs/Part4/5.6.2/ Sessions are terminated by the Server automatically if the Client fails to issue a Service request on the Session within the timeout period negotiated by the Server in the CreateSession Service response. This protects the Server against Client failures and against situations where a failed underlying connection cannot be re-established. Clients shall be prepared to submit requests in a timely manner to prevent the Session from closing automatically. Clients may explicitly terminate Sessions using the CloseSession Service.

if you want to let the client run 24/7 you need to structure your code differently! do you want to read data ("cyclic" or by "subscription")? if you want to connect to a "cloud opc ua server" / "opc ua server in the internet" you also have to monitor the connection and handle timeouts (reconnect if connection breaks)

AndreasHeine commented 4 years ago

read only once:

from opcua import Client
client = Client("opc.tcp://192.168.10.1:4840")
client.connect()
print("Connected")
node = client.get_node("ns=0;i=2267")
node_value = node.get_value()
print(node_value)
client.disconnect()

read each 5 seconds:

from opcua import Client
from time import sleep
client = Client("opc.tcp://192.168.10.1:4840")
client.connect()
print("Connected")
node = client.get_node("ns=0;i=2267")
while 1:
    #endless loop / runs forever
    node_value = node.get_value()
    print(node_value)
    sleep(5)
AndreasHeine commented 4 years ago

subscription example ("real time"):

from opcua import Client

class SubHandler(object):
    def datachange_notification(self, node, val, data):
        print("New data change ", node, val)

if __name__ == "__main__":
    client = Client("opc.tcp://192.168.10.1:4840")
    client.connect()
    myvar = client.get_node("ns=0;i=2267")
    handler = SubHandler()
    sub = client.create_subscription(500, handler)
    handle = sub.subscribe_data_change(myvar)

every 500ms the client sends a publish request and if any subscribed node/nodes has changed within the last 500ms the server will send a publish response with the changed data or a keep-alive

Sora0224 commented 4 years ago

Thanks for the reply Andreas, so my coding is already real time, but it will timeout after 60000ms, the server is always online and I just need my program to keep on taking the data from the server.

`from opcua import Client import time from datetime import datetime

client = Client("opc.tcp://10.3.1.222:49320") client.connect()

while True: Tag_Success = client.get_node("ns=2;s=x") Tagvalue = Tag_L1S.get_value() Tag_Cuff = client.get_node("ns=2;s=x") Tagvalue_L1C = Tag_L1C.get_value() time.sleep(1)`

The program is working (this is just part of the coding) but it will timeout after 60000ms and the time taken for each cycle always different, sometimes it's very fast and sometimes it took like 15s.

Sora0224 commented 4 years ago

I also tried the client.session_timeout = 600000

I still get

Requested session timeout to be 3600000ms, got 60000ms instead

hmmm I'm still looking into it.

AndreasHeine commented 4 years ago

a connection thru the internet is always fuzzy... maybe its a networking problem! i would recommend you using asyncua (python-opcua in async) for clients instead and use a subscription to get the data (polling is not really "realtime" by definition)

from opcua import Client
import time

client = Client("opc.tcp://192.168.10.1:4840")
client.session_timeout = 30000 #in my case i connect to a s7-1500 which has a timeout 30s and the warning disappears with this line
client.connect()
print("Connected")
node = client.get_node("ns=0;i=2267")
while 1:
    #endless loop / runs forever
    node_value = node.get_value()
    print(node_value)
    time.sleep(5)

maybe this snipet helps you as quickstart:

import asyncio, json
from asyncua import Client, ua, Node
from asyncua.common.events import Event
from datetime import datetime

####################################################################################
# Globals:
####################################################################################

# OPC UA Client
server_url = "opc.tcp://192.168.10.1:4840"
nodes_to_subscribe =    [
                        #node-id
                        "ns=4;i=1000", 
                        "ns=0;i=2267", 
                        "ns=0;i=2259",                       
                        ]
events_to_subscribe =   [
                        #(eventtype-node-id, event-node-id)
                        ]

####################################################################################
# OpcUaClient:
####################################################################################

class SubscriptionHandler:
    """
    The SubscriptionHandler is used to handle the data that is received for the subscription.
    """
    def datachange_notification(self, node: Node, val, data):
        """
        Callback for asyncua Subscription.
        This method will be called when the Client received a data change message from the Server.
        """
        print(node, val, data)

    def event_notification(self, event: Event):
        """
        called for every event notification from server
        """
        print(event)

    def status_change_notification(self, status):
        """
        called for every status change notification from server
        """
        print(status)

async def opcua_client():
    """
    -handles connect/disconnect/reconnect/subscribe/unsubscribe
    -connection-monitoring with cyclic read of the service-level
    """
    client = Client(url=server_url)
    handler = SubscriptionHandler()
    subscription = None
    case = 0
    subscription_handle_list = []
    while 1:
        if case == 1:
            #connect
            print("connecting...")
            try:
                await client.connect()
                await client.load_type_definitions()
                print("connected!")
                case = 2
            except:
                print("connection error!")
                case = 1
                await asyncio.sleep(5)
        elif case == 2:
            #subscribe all nodes and events
            print("subscribing nodes and events...")
            try:
                subscription = await client.create_subscription(50, handler)
                subscription_handle_list = []
                if nodes_to_subscribe:
                    for node in nodes_to_subscribe:
                        handle = await subscription.subscribe_data_change(client.get_node(node))
                        subscription_handle_list.append(handle)
                if events_to_subscribe:
                    for event in events_to_subscribe:
                        handle = await subscription.subscribe_events(event[0], event[1])
                        subscription_handle_list.append(handle)
                print("subscribed!")
                case = 3
            except:
                print("subscription error")
                case = 4
                await asyncio.sleep(0)
        elif case == 3:
            #running => read cyclic the service level if it fails disconnect and unsubscribe => wait 5s => connect
            try:
                service_level = await client.get_node("ns=0;i=2267").get_value()
                if service_level >= 200:
                    case = 3
                else:
                    case = 4
                await asyncio.sleep(5)
            except:
                case = 4
        elif case == 4:
            #disconnect clean = unsubscribe, delete subscription then disconnect
            print("unsubscribing...")
            try:
                if subscription_handle_list:
                    for handle in subscription_handle_list:
                        await subscription.unsubscribe(handle)
                await subscription.delete()
                print("unsubscribed!")
            except:
                print("unsubscribing error!")
                subscription = None
                subscription_handle_list = []
                await asyncio.sleep(0)
            print("disconnecting...")
            try:
                await client.disconnect()
            except:
                print("disconnection error!")
            case = 0
        else:
            #wait
            case = 1
            await asyncio.sleep(5)

####################################################################################
# Run:
####################################################################################

if __name__ == "__main__":
    asyncio.ensure_future(opcua_client())
    asyncio.get_event_loop().run_forever()
Sora0224 commented 4 years ago

Let me study the code and asyncio first, not familiar with the free opcua function. Thanks mate!

davesliu commented 3 years ago

To fix the timeout warning issue. It should expose the session_timeout para in init function of client.py and set the session_timeout to 60000 by using Client(url, session_timeout=60000).

def init(self, url, timeout=4, session_timeout=3600000, secure_channel_timeout=3600000): ... ... self.secure_channel_timeout = secure_channel_timeout # 1 hour self.session_timeout = session_timeout # 1 hour ... ...

AndreasHeine commented 3 years ago

client.session_timeout = ... before connect should do the trick those properties can be accessed by the class instance itself!

RodriAliaga commented 6 months ago

client.session_timeout = ... before connect should do the trick those properties can be accessed by the class instance itself!

It worked for me like that way, thanks @AndreasHeine

Screenshot 2024-04-17 085405