FreeOpcUa / python-opcua

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

Using FreeOpcUa for OPC DA server #489

Closed abhinavrawat27 closed 7 years ago

abhinavrawat27 commented 7 years ago

Hi all,

I have a OPC DA server running on it. I have tested it on windows with client software and is working fine. I need make an application which will connect to this OPC DA server from linux. Is it possible to use this FreeOPCUA python for DA. Is this library compatible with OPC DA.

Thanks

zerox1212 commented 7 years ago

OPC DA is a completely different protocol than OPC UA as far as I know.

eskildsf commented 7 years ago

You can use a proxy to convert the OPC-DA interface into a OPC-UA interface. However, this proxy has to run on a Windows computer since OPC-DA is only implemented on Windows.

abhinavrawat27 commented 7 years ago

Hi eskildsf

Can you please mention any proxy software to convert DA to UA. Is it compatible with windows 10.?

eskildsf commented 7 years ago

I have implemented a proxy in Python using OpenOPC and python-opcua, see http://courses.compute.dtu.dk/02619/software/opcda_to_opcua.py.

Commercial alternatives are also available since this is a relatively common problem. See https://opcfoundation.org/products/view/uagateway or https://opcfoundation.org/products/view/opc-ua-proxy.

dushyantbangal commented 5 years ago

@eskildsf I'm trying to use your wrapper. Its connecting to the DA server, scanning the tags. But for some reason, its crashing at c.properties(node)

Traceback (most recent call last): File "opcda_to_opcua.py", line 135, in for id, description_of_id, value in c.properties(node): File "C:\Users\Dushyant\Miniconda3\lib\site-packages\OpenOPC.py", line 1004, in properties return list(props) File "C:\Users\Dushyant\Miniconda3\lib\site-packages\OpenOPC.py", line 937, in iproperties property_id = [p for p, d in tag_properties if p > 0] File "C:\Users\Dushyant\Miniconda3\lib\site-packages\OpenOPC.py", line 937, in property_id = [p for p, d in tag_properties if p > 0] TypeError: 'NoneType' object is not callable

I'm using python 3.7 32-bit (as the Matrikon Simulator is also 32-bit, and using x64 was resulting in different error mentioned here

eskildsf commented 5 years ago

Dear @dushyantbangal,

I am a bit confused. OpenOPC is compatible with Python 2.7 32 bit. How did you manage to use it in Python 3.7?

I have installed Anaconda 2.7 32 bit and Matrikon OPC Simulation server to test if it works and I found a different bug due to the SImulation server. The server exposes tags with values corresponding to dates before the year 1900 which cannot be formatted as a string (see https://sourceforge.net/p/openopc/discussion/709251/thread/1075b4b4/). This is why you get an error in line 937 of OpenOPC.py because it attempts to cast the value of the tag as a string. This type conversion fails and causes the entire code to error.

Removing the nodes "Random.Time", "Bucket Brigade.Time", "Write Only.Time" and "Write Error.Time" resolves the issue. To do this, add the following snippet:

nodes.remove(u'Bucket Brigade.Time')
nodes.remove(u'Random.Time')
nodes.remove(u'Write Error.Time')
nodes.remove(u'Write Only.Time')

before for node in nodes: in opcda_to_opcua.py.

This error is differen't from yours. Can you reproduce the same results as me with a similar setup?

Br Eskild

dushyantbangal commented 5 years ago

Hello @eskildsf,

I found https://pypi.org/project/OpenOPC-Python3x/ , and just assumed I need to use 3.7.

Now I've done the setup as asked by you, and I'm getting the following error:

Traceback (most recent call last): File "opcda_to_opcua.py", line 125, in tree[path] = parent.add_folder(idx,folder) File "C:\Users\Dushyant\Anaconda2\lib\site-packages\opcua\common\node.py", line 654, in add_folder return opcua.common.manage_nodes.create_folder(self, nodeid, bname) File "C:\Users\Dushyant\Anaconda2\lib\site-packages\opcua\common\manage_nodes.py", line 41, in create_folder return node.Node(parent.server, _create_object(parent.server, parent.nodeid, nodeid, qname, ua.ObjectIds.FolderType)) File "C:\Users\Dushyant\Anaconda2\lib\site-packages\opcua\common\manage_nodes.py", line 165, in _create_object attrs.Description = ua.LocalizedText(qname.Name) File "C:\Users\Dushyant\Anaconda2\lib\site-packages\opcua\ua\uatypes.py", line 522, in init self.Text = text File "C:\Users\Dushyant\Anaconda2\lib\site-packages\opcua\ua\uatypes.py", line 533, in Text raise ValueError("A LocalizedText object takes a string as argument, not a {}, {}".format(type(text), text)) ValueError: A LocalizedText object takes a string as argument, not a <type 'unicode'>, Random

The error points to tree[path] = parent.add_folder(idx,folder) in my case.

Also, to avoid any other issues, I'm just passing the following nodes to the for loop. [u'Random.Int1', u'Random.Int2', u'Random.Int4']

dushyantbangal commented 5 years ago

Hi @eskildsf,

I just had to add .encode("utf-8") to the folder and file variables, and it worked! Thanks! :)

cjunze commented 4 years ago

Dear @eskildsf

I am using your proxy, and tested. It works greatly. Thank you! But I got I issues, which is when I add a new Tag on OPC_DA server, the new tag will not be published to the UA server unless I restart the program. So I add “while True” to loop the program. Then I got another issue: I got so many the same folder and tag on UA I fixed the folder’s problem by putting the tree{} out of the while loop. However, I can’t fix the tag problem. Could anyone help me please. Thanks a lot!

eskildsf commented 4 years ago

Hi @cjunze,

Sure, I'll try to help you. I am a little bit reluctant though because it appears that you are asking for help regarding your solution to a problem but not the problem itself. I haven't come across a use case before where a dynamic OPC-DA tag space was necessary. Please elaborate on the problem you are trying to solve so I can help you better.

On the face of it there really is no reason why you shouldn't be able to dynamically synchronize the OPC-UA tag space to the OPC-DA tag space. I just can't think of an application where it is relevant.

Have a great day.

Br Eskild

cjunze commented 4 years ago

Hi @eskildsf First at all, i appreciate your response.

Here is my case: There are a huge amount of products running with OPC-DA, which products are published many years ago, and only support with OPC-DA. As you know, with the development More and More products will run with OPC-UA. In order to make the manage more convenient, i need to use UA ONLY to read or write. This is the reason why i need the dynamically synchronize DA to UA. Thank you sooo much!

Happy Christmas Eve~

By Cjunze

oroulet commented 4 years ago

@cjunze in that case the main issue is that you need to tech yourself more python/programming. Then you will understand that solving your problem is easy

dushyantbangal commented 4 years ago

I wanted to monitor 1000+ tags, so my DA server kept freezing on me. Must be an issue from the server side itself.

pitch3k commented 4 years ago

Hi @eskildsf,

I just had to add .encode("utf-8") to the folder and file variables, and it worked! Thanks! :)

Hi @dushyantbangal, could you please help me. I'm new with python. I would like tor runa an OPC-DA to OPC-UA wrapper. I'm running in the same error as @eskildsf. can you show me where to add .Encode() in the opcda_to_opcua.py code. Could you please send me your corrected file ? Thanks a lot

eskildsf commented 4 years ago

Hi @eskildsf, I just had to add .encode("utf-8") to the folder and file variables, and it worked! Thanks! :)

Hi @dushyantbangal, could you please help me. I'm new with python. I would like tor runa an OPC-DA to OPC-UA wrapper. I'm running in the same error as @eskildsf. can you show me where to add .Encode() in the opcda_to_opcua.py code. Could you please send me your corrected file ? Thanks a lot

Hi @pitch3k,

I suppose the change that @dushyantbangal refers to is to append .encode("utf-8") at the end of lines 110 and 111 in the opcda_to_opcua.py script.

Br Eskild

pitch3k commented 4 years ago

Hi @eskildsf, I just had to add .encode("utf-8") to the folder and file variables, and it worked! Thanks! :)

Hi @dushyantbangal, could you please help me. I'm new with python. I would like tor runa an OPC-DA to OPC-UA wrapper. I'm running in the same error as @eskildsf. can you show me where to add .Encode() in the opcda_to_opcua.py code. Could you please send me your corrected file ? Thanks a lot

Hi @pitch3k,

I suppose the change that @dushyantbangal refers to is to append .encode("utf-8") at the end of lines 110 and 111 in the opcda_to_opcua.py script.

Br Eskild

Hi @eskildsf , thanks for that hint. I think tonight I found another solution. Just before the error raised (in uatypes.py line 540 I changed "str" to "basestring". It seems to work now

grafik

eskildsf commented 4 years ago

Thank you for sharing your solution, @pitch3k. If you feel like it you can contribute your edit of uatypes.py in a pull request. I'm sure the manitainers would appreciate it.

Br Eskild

enesebastian commented 4 years ago

hello @eskildsf , could you please explain to me the SubscriptionHandler class?

eskildsf commented 4 years ago

hello @eskildsf , could you please explain to me the SubscriptionHandler class?

Hi @enesebastian. The code is only 160 lines so I'd recommend that you read it to gain an understanding. I recommend you to consult the python-freeopcua source code concerning subscriptions and how they are implemented in the library.

If you have concrete questions then you are welcome to ask them here so future readers can benefit as well.

Br Eskild

dushyantbangal commented 4 years ago

@pitch3k My changes were on lines 120 and 137

tree[path] = parent.add_folder(idx,folder.encode("utf-8"))

opcua_node = tree[path].add_variable(idx, file.encode("utf-8"), ua.Variant(current_value, ua.VariantType.UInt16))

Though, it might work on 110 and 111 as @eskildsf mentioned, but I haven't tried.

enesebastian commented 4 years ago

hello @eskildsf , could you please explain to me the SubscriptionHandler class?

Hi @enesebastian. The code is only 160 lines so I'd recommend that you read it to gain an understanding. I recommend you to consult the python-freeopcua source code concerning subscriptions and how they are implemented in the library.

If you have concrete questions then you are welcome to ask them here so future readers can benefit as well.

Br Eskild

hey, I did go through the source code of opcua ( they dont go into details with their methods and classes ) but I didn't really understand what you did in that SubscriptionHandler class. What is i, n and what is the purpose of the final_datachange_notification method ? Thank you for answering !

eskildsf commented 4 years ago

Hi @enesebastian,

I'll give it a shot.

Subscriptions are a key feature of OPC-UA and allow nodes to be monitored without polling. In python-opcua subscriptions on a server can be registered by the create_subscription method of the opcua.Server class. The create_subscriptionmethod needs to know what to do when an event occours and this is exactly what the SubscriptionHandler class handles (pun intended). In the present code the subscriptions are registered on lines 148 to 149:

handler = SubscriptionHandler(len(writeable_variable_handles))
sub = server.create_subscription(100, handler).subscribe_data_change(writeable_variable_handles.values())

On the first line, the SubscriptionHandler class is instantiated with n=len(writeable_variable_handles), so n is really just the number of monitored variables. The purpose of the subscription is to monitor writable nodes for changes and then to write those changes to the OPC-DA server.

So when might a subscription trigger? It triggers when the value of the subscribed node is changed but as it turns out it also triggers for newly added nodes. This means that immediately when the subscriptions are registered in lines 148-149 then we immediately get n calls to the SubscriptionHandler.datachange_notification method. We only want to forward actual changes so if we ignore the first n subscription events and handle any future writes by a different method. This is why you see two methods in the SubscriptionHandler class. The datachange_notification method will catch events for newly added nodes. Once all n nodes have emitted an event then the method is substituted by final_datachange_notification which will actually propagate the event to the OPC-DA server.

Br Eskild

enesebastian commented 4 years ago

Hi @enesebastian,

I'll give it a shot.

Subscriptions are a key feature of OPC-UA and allow nodes to be monitored without polling. In python-opcua subscriptions on a server can be registered by the create_subscription method of the opcua.Server class. The create_subscriptionmethod needs to know what to do when an event occours and this is exactly what the SubscriptionHandler class handles (pun intended). In the present code the subscriptions are registered on lines 148 to 149:

handler = SubscriptionHandler(len(writeable_variable_handles))
sub = server.create_subscription(100, handler).subscribe_data_change(writeable_variable_handles.values())

On the first line, the SubscriptionHandler class is instantiated with n=len(writeable_variable_handles), so n is really just the number of monitored variables. The purpose of the subscription is to monitor writable nodes for changes and then to write those changes to the OPC-DA server.

So when might a subscription trigger? It triggers when the value of the subscribed node is changed but as it turns out it also triggers for newly added nodes. This means that immediately when the subscriptions are registered in lines 148-149 then we immediately get n calls to the SubscriptionHandler.datachange_notification method. We only want to forward actual changes so if we ignore the first n subscription events and handle any future writes by a different method. This is why you see two methods in the SubscriptionHandler class. The datachange_notification method will catch events for newly added nodes. Once all n nodes have emitted an event then the method is substituted by final_datachange_notification which will actually propagate the event to the OPC-DA server.

Br Eskild

Thank you very much for the explanation !

telepzk commented 3 years ago

If anyone helps, in the new version of python-opcua on line 78 replace: path_as_string = node.get_path_as_string() with: path_as_string = node.get_path(as_string=True)

AliRaza0304444 commented 3 years ago

Hello Everyone, I'm new to python and trying to implement the code. I'm currently using python 3.9.4 32 bit I'm getting the following error Endpoints other than open requested but private key and certificate are not set. Listening on 172.16.6.208:8080 Traceback (most recent call last): File "C:\Users\alraza\AppData\Local\Programs\Python\Python39-32\lib\multiprocessing\queues.py", line 245, in _feed obj = _ForkingPickler.dumps(obj) File "C:\Users\alraza\AppData\Local\Programs\Python\Python39-32\lib\multiprocessing\reduction.py", line 51, in dumps cls(buf, protocol).dump(obj) _pickle.PicklingError: Can't pickle <class 'pywintypes.datetime'>: attribute lookup datetime on pywintypes failed Traceback (most recent call last): File "C:\Users\alraza\OPCDA-OPCUA.py", line 168, in for reading in c.read(readables): File "C:\Users\alraza\AppData\Local\Programs\Python\Python39-32\lib\site-packages\OpenOPC.py", line 625, in read return list(results) File "C:\Users\alraza\AppData\Local\Programs\Python\Python39-32\lib\site-packages\OpenOPC.py", line 543, in iread raise TimeoutError('Callback: Timeout waiting for data') OpenOPC.TimeoutError: Callback: Timeout waiting for data

AliRaza0304444 commented 3 years ago

Hello Everyone, I'm new to python and trying to implement the code. I'm currently using python 3.9.4 32 bit I'm getting the following error Endpoints other than open requested but private key and certificate are not set. Listening on 172.16.6.208:8080 Traceback (most recent call last): File "C:\Users\alraza\AppData\Local\Programs\Python\Python39-32\lib\multiprocessing\queues.py", line 245, in _feed obj = _ForkingPickler.dumps(obj) File "C:\Users\alraza\AppData\Local\Programs\Python\Python39-32\lib\multiprocessing\reduction.py", line 51, in dumps cls(buf, protocol).dump(obj) _pickle.PicklingError: Can't pickle <class 'pywintypes.datetime'>: attribute lookup datetime on pywintypes failed Traceback (most recent call last): File "C:\Users\alraza\OPCDA-OPCUA.py", line 168, in for reading in c.read(readables): File "C:\Users\alraza\AppData\Local\Programs\Python\Python39-32\lib\site-packages\OpenOPC.py", line 625, in read return list(results) File "C:\Users\alraza\AppData\Local\Programs\Python\Python39-32\lib\site-packages\OpenOPC.py", line 543, in iread raise TimeoutError('Callback: Timeout waiting for data') OpenOPC.TimeoutError: Callback: Timeout waiting for data

I have succesfully implemented OPC-DA to OPC-UA wrapper code with python 3.9 version with 32 bit. Since the OpenOPC library is only supported by 32 bit python and not with 64 bit. In order to successfully run the code add the following lines after from opcua import ua, uamethod, Server import pywintypes pywintypes.datetime = pywintypes.TimeType

Thank you

duduyoyo commented 4 months ago

This solution makes your life easier since it retrieves DA data back in JSON - so you don't even need convert it to UA and consume it directly in any way you want. It works with any recent 64bit Python release. Hope it can help!