Closed kashp closed 8 years ago
I don't understand your question. Do you want to run code on PLC or comunicate with PLC through OPC UA?
This library is only the OPC UA side of things. The only way you could connect directly with a PLC is if your PLC has a built in OPC UA client or server. If you want to use Python OPC UA like Kepserver you need to write your own driver as a bridge between PLC and OPC UA server. Depending on your PLC it could be rather easy (like if you need to talk Modbus RTU) to extremely difficult (Allen Bradly CIP).
I have used Python Modbus_tk to make a Modbus RTU to OPC UA bridge and it wasn't that difficult. The Modbus master polls the PLC in its own thread, the OPC UA server grabs the data and updates the OPC UA nodes.
@kashp did you find solution?
@zerox1212 have you released that bridge anywhere? And if not, would you mind doing so? I'm a complete newbie to the OPC UA and I'm trying to connect a Eurotherm Mini8 PLC with Modbus TCP via OPC UA to a control system based on Tango Controls (open source framework, used in synchrotrons). Getting through the "lower" side of things (Modbus to OPC UA server) with your help would be an extreme speed-up in my work.
I never released it because my implementation is extremely specific (hard coded slaves and messages). However I can probably make a gist of a few parts of my code when I get some time if that would help you.
Yes, that would surely help - thanks! Will you post the links here once it's ready?
https://github.com/zerox1212/python-opcua-modbus-driver
I didn't actually run this code, but maybe you can take a look at it and get an idea how I did it.
It does have some more advanced concepts, that you will need to understand because it was faster to copy paste my code than write you an example from scratch. The main thing you need to know is that my implementation tries to sync a UA Object to a matching python class. That is why there is a Device UA object and a Device
python class. They get unified via theUaObject
super class.
You need to edit the slaves
objects in modbus to match your modbus situation. Then you need to edit the XML file that defines MyDevice
UA object with the slave address. That way the python Device
object can collect the data from the driver when it calls device.update()
.
I had many slaves and wanted to build my application dynamically via XML with a generic structure, so this is probably over kill for what you need. Let me know if you have questions.
What the Device object looks like in UA (python class is identical to this)
If you have a slave address 1, that you read a single INT from you need to set MyDevices.DataAddress to "1:1" and DataSource to "ModbusRTU"
That is amazing, thank you so much!
I hope it helps you. Probably the best way to approach your solution is to design a UA object type that matches your Eurotherm Mini8 PLC, or perhaps some how similar to how that PLC has its data organized. Then you could modify my code to "mirror" everything in the PLC to OPC UA.
I've worked with your code (thanks again!), wrote my own ModbusTcp driver (since I've got the PLC connected via Ethernet) and I've encountered 2 issues so far:
I have a single ModbusTcp slave. As far as I understand, if I want to get many parameters from it, I have to create a Device OPC object for every single one of them, because there's only one DataAddress defined in the Device type. Or should I define more properties/variables in the Device type?
I get an Exception from inside the package when the first Device object is created. It's caused by creating a subscription in your UaObject (I haven't modified it). The traceback is included below.
It seems that the ThreadLoop's call_later()
should never be called before its run()
method since the loop attribute is created in run()
method and not in the constructor (as it possibly should be?).
Is it a bug or I'm doing something wrong?
Traceback (most recent call last):
File "/home/admin/PycharmProjects/lib-agh-modbus2opcua/m2opc_server/server.py", line 157, in <module>
main()
File "/home/admin/PycharmProjects/lib-agh-modbus2opcua/m2opc_server/server.py", line 142, in main
my_server = OPCUAServer(logger)
File "/home/admin/PycharmProjects/lib-agh-modbus2opcua/m2opc_server/server.py", line 66, in __init__
device = Device(self.server, device_node, self.modbus_driver, logger)
File "/home/admin/PycharmProjects/lib-agh-modbus2opcua/m2opc_server/device.py", line 14, in __init__
super(Device, self).__init__(opcua_server, ua_node)
File "/home/admin/PycharmProjects/lib-agh-modbus2opcua/m2opc_server/ua_object.py", line 27, in __init__
sub = opcua_server.create_subscription(500, handler)
File "/usr/lib/python2.7/site-packages/opcua/server/server.py", line 323, in create_subscription
return Subscription(self.iserver.isession, params, handler)
File "/usr/lib/python2.7/site-packages/opcua/common/subscription.py", line 88, in __init__
response = self.server.create_subscription(params, self.publish_callback)
File "/usr/lib/python2.7/site-packages/opcua/server/internal_server.py", line 341, in create_subscription
result = self.subscription_service.create_subscription(params, callback)
File "/usr/lib/python2.7/site-packages/opcua/server/subscription_service.py", line 33, in create_subscription
sub.start()
File "/usr/lib/python2.7/site-packages/opcua/server/internal_subscription.py", line 270, in start
self._subscription_loop()
File "/usr/lib/python2.7/site-packages/opcua/server/internal_subscription.py", line 279, in _subscription_loop
self.subservice.loop.call_later(self.data.RevisedPublishingInterval / 1000.0, self._sub_loop)
File "/usr/lib/python2.7/site-packages/opcua/common/utils.py", line 162, in call_later
p = functools.partial(self.loop.call_later, delay, callback)
AttributeError: 'NoneType' object has no attribute 'call_later'
you need to start server, before subscribing
On Fri, 25 Nov 2016 at 09:26 Łukasz Dudek notifications@github.com wrote:
I've worked with your code (thanks again!), wrote my own ModbusTcp driver (since I've got the PLC connected via Ethernet) and I've encountered 2 issues so far:
1.
I have a single ModbusTcp slave. As far as I understand, if I want to get many parameters from it, I have to create a Device OPC object for every single one of them, because there's only one DataAddress defined in the Device type. Or should I define more properties/variables in the Device type? 2.
I get an Exception from inside the package when the first Device object is created. It's caused by creating a subscription in your UaObject (I haven't modified it). The traceback is included below. It seems that the ThreadLoop's call_later() should never be called before its run() method since the loop attribute is created in run() method and not in the constructor (as it possibly should be?). Is it a bug or I'm doing something wrong?
Traceback (most recent call last): File "/home/admin/PycharmProjects/lib-agh-modbus2opcua/m2opc_server/server.py", line 157, in
main() File "/home/admin/PycharmProjects/lib-agh-modbus2opcua/m2opc_server/server.py", line 142, in main my_server = OPCUAServer(logger) File "/home/admin/PycharmProjects/lib-agh-modbus2opcua/m2opc_server/server.py", line 66, in init device = Device(self.server, device_node, self.modbus_driver, logger) File "/home/admin/PycharmProjects/lib-agh-modbus2opcua/m2opc_server/device.py", line 14, in init super(Device, self).init(opcua_server, ua_node) File "/home/admin/PycharmProjects/lib-agh-modbus2opcua/m2opc_server/ua_object.py", line 27, in init sub = opcua_server.create_subscription(500, handler) File "/usr/lib/python2.7/site-packages/opcua/server/server.py", line 323, in create_subscription return Subscription(self.iserver.isession, params, handler) File "/usr/lib/python2.7/site-packages/opcua/common/subscription.py", line 88, in init response = self.server.create_subscription(params, self.publish_callback) File "/usr/lib/python2.7/site-packages/opcua/server/internal_server.py", line 341, in create_subscription result = self.subscription_service.create_subscription(params, callback) File "/usr/lib/python2.7/site-packages/opcua/server/subscription_service.py", line 33, in create_subscription sub.start() File "/usr/lib/python2.7/site-packages/opcua/server/internal_subscription.py", line 270, in start self._subscription_loop() File "/usr/lib/python2.7/site-packages/opcua/server/internal_subscription.py", line 279, in _subscription_loop self.subservice.loop.call_later(self.data.RevisedPublishingInterval / 1000.0, self._sub_loop) File "/usr/lib/python2.7/site-packages/opcua/common/utils.py", line 162, in call_later p = functools.partial(self.loop.call_later, delay, callback) AttributeError: 'NoneType' object has no attribute 'call_later' — You are receiving this because you modified the open/close state. Reply to this email directly, view it on GitHub https://github.com/FreeOpcUa/python-opcua/issues/116#issuecomment-262904014, or mute the thread https://github.com/notifications/unsubscribe-auth/ACcfzqg4IthWekJP6YXRO5PAPFXmO3roks5rBpu-gaJpZM4HGyv7 .
Oh, that seems obvious, of course! Now it's not crashing, thank you!
Okay, so I've got the server up and running and connected to my PLC. Now, I've got another question: how should I approach writing parameters through OPC to Modbus?
Sorry about the subscribe thing, my example code is wrong. This code should be in a different function that gets called after server is started.
# get Objects node
objects = self.server.get_objects_node()
# get the device object type so we can find all the devices in the address space
hw_type = self.server.nodes.base_object_type.get_child("2:Device")
device_nodes = find_o_types(objects, hw_type)
# instantiate a device python object and make it an attribute of the server class
# FIXME do your own organization, most likely an object oriented model
for device_node in device_nodes:
device = Device(self.server, device_node)
setattr(self, device.b_name, device)
# keep track of the device because so that we can update it cyclically using the driver
self.devices.append(device)
My code was for a read only scenario. You probably want to start looking at https://github.com/FreeOpcUa/python-opcua/issues/365 because it's going to be the same issue you face. You might need to get creative.
Hi I am new to PLC, Can i use your server code as a replacement of kepserver, can your code directly communicate with plc, is there any specific driver support ??