Multiple instances of a library lead to errors if same input parameter

Hi all,
for my test, I must create multiple OPC UA instances. My OPC UA library is based on python’s opcua. I have to call the library in RF with different amount of input parameter in order to make two individual instances. In other words, if I call my Library two times with same input parameter, I will not get a second intance.

Example:

# When create two instances of a python class with same inputs, RF only creates one instance.
# Therefore, the second instance is created with different inputs, because two OPC UA 
# connections are needed in test.
 Library           OPCUA/OPCUA.py    ${DUT_IP}    AS   OpcuaClient1      
 Library           OPCUA/OPCUA.py    ${DUT_IP}    ${4840}    AS   OpcuaClient2     

My OPC UA class would be:

class OPCUA():
"""
Get access to OPCUA interface
"""
ROBOT_LIBRARY_SCOPE = "Global"
ROBOT_LIBRARY_VERSION = "0.3"

deco.not_keyword # Disable keywords
def __init__(self, ip:str  = "None", port:str = "4840", user:str = "", password:str = "", policy:str = "", secMode:str = ""):
    """
    Method: __init__ - intended for internal use\n
    Description: Constructor of this class which mainly establishes the connection to\n
                 the OPC interface\n
    Parameter:\n
        ip: ip address of your system\n
        port: port of your system\n
        user: Username of opc ua user from ATG\n
        password: Password of opc ua user from ATG\n
        policy: policy like Basic256Sha256\n
        secMode: signature mode like Signe or SignAndEncrypt\n
    Example: OPCUA("192.168.1.2", 4840, opcUser, opcUserPassword, Basic256Sha256, SignAndEncrypt)
    """
    self.ip = ip
    self.port = port
    self.user = user
    self.password = password
    self.policy = policy
    self.secMode = secMode
    self.clientList = []
    if ip != 'None':
        self._connect()
        self.clientList.append(self.client)
def _connect(self):
    """
    Method: _connect - intended for internal use\n
    Description: Establish the connection to interface\n
    """

    self.client = Client("opc.tcp://" + self.ip + ":" + self.port)
    self.client.session_timeout = SESSIONTIMEOUT # The following timeout setting disables a warning using Asset Monitor
    if self.user == "" and self.password == "":
        # user wants an anonymous login
        pass
    else:
        # user uses security policies and credentials
        self.client.set_user(self.user)
        self.client.set_password(self.password)
    if self.policy == "" or self.secMode == "":
        # user wants no security policy
        pass
    else:
        self.client.application_uri = URN
        self.client.set_security_string("Basic256Sha256,SignAndEncrypt,C:\\XYZ.pem,C:\\XYZ.pem") #AndEncrypt
    try:
        self.client.connect()
        logger.console("Successfully connected to OPCUA Server")
        return 0
    except ua.UaError as e:
        myPrint(f"OPC UA Error code: {e.args[0]}") #DEBUG
        return e.args[0]
    except ConnectionAbortedError as e:
        myPrint(f"ConnectionAbortedError: {e.args[0]}") #DEBUG
        return -1
    except concurrent.futures._base.CancelledError as e:
        myPrint("CancelledError") #DEBUG
        return -1
    #finally:
    #    pass

What do I get wrong here?

Thanks in advance.

I think there can only be once instance even if you use AS someOtherName

SeleniumLibrary handles this by having multiple instances of “driver” as class attribute and switching between those happens with Switch Browser keyword. My suggestion is to use similar approach…

Hello,
I think Since self.clientList.append(self.client) is used, if you’re calling OPCUA twice with the same parameters, the client object is the same. This is because Python uses the same instance in the clientList since the parameters you pass to the constructor are identical, resulting in only one connection being created.

You need to ensure that every time you create a new OPCUA instance, a new Client object is created with unique parameters. You can use self.clientList to store multiple client instances, but you should make sure each new instance is independent.
class OPCUA():
“”"
Get access to OPCUA interface
“”"
ROBOT_LIBRARY_SCOPE = “Global”
ROBOT_LIBRARY_VERSION = “0.3”

deco.not_keyword # Disable keywords

def __init__(self, ip: str = "None", port: str = "4840", user: str = "", password: str = "", policy: str = "", secMode: str = ""):
    """
    Method: __init__ - intended for internal use\n
    Description: Constructor of this class which mainly establishes the connection to\n
                 the OPC interface\n
    Parameter:\n
        ip: ip address of your system\n
        port: port of your system\n
        user: Username of opc ua user from ATG\n
        password: Password of opc ua user from ATG\n
        policy: policy like Basic256Sha256\n
        secMode: signature mode like Signe or SignAndEncrypt\n
    Example: OPCUA("192.168.1.2", 4840, opcUser, opcUserPassword, Basic256Sha256, SignAndEncrypt)
    """
    self.ip = ip
    self.port = port
    self.user = user
    self.password = password
    self.policy = policy
    self.secMode = secMode
    self.clientList = []  # Keeps track of all client connections
    
    # Always create a new client instance if ip is not 'None'
    if ip != 'None':
        self._connect()
        self.clientList.append(self.client)

def _connect(self):
    """
    Method: _connect - intended for internal use\n
    Description: Establish the connection to interface\n
    """

    self.client = Client("opc.tcp://" + self.ip + ":" + self.port)
    self.client.session_timeout = SESSIONTIMEOUT # The following timeout setting disables a warning using Asset Monitor
    
    # If no user/password, then try anonymous login
    if self.user == "" and self.password == "":
        pass
    else:
        self.client.set_user(self.user)
        self.client.set_password(self.password)
    
    # Set security parameters if provided
    if self.policy and self.secMode:
        self.client.application_uri = URN
        self.client.set_security_string("Basic256Sha256,SignAndEncrypt,C:\\XYZ.pem,C:\\XYZ.pem")
    
    try:
        # Attempt connection to the server
        self.client.connect()
        logger.console("Successfully connected to OPCUA Server")
        return 0
    except ua.UaError as e:
        myPrint(f"OPC UA Error code: {e.args[0]}") # DEBUG
        return e.args[0]
    except ConnectionAbortedError as e:
        myPrint(f"ConnectionAbortedError: {e.args[0]}") # DEBUG
        return -1
    except concurrent.futures._base.CancelledError as e:
        myPrint("CancelledError") # DEBUG
        return -1

New client per instance:
I’ve added the condition to ensure that a new Client object is always created when a new OPCUA instance is initialized, regardless of whether the parameters are identical or not.
Adding to clientList: You continue to add each created client to the clientList. This will keep track of all active connections.
Best Regard,
Diana
Hot Schedules