python-opcua
python-opcua copied to clipboard
Writing values from subscribed node into dataframe
Hello,
i would like to write my values, i get from get_value into a dataframe. As far as i know, i need to define this in the datachange_notification definition. However, if i try to call get_value inside of the definition, i end up with the following error:
ERROR:opcua.common.subscription:Exception calling data change handler Traceback (most recent call last): File "C:\Users\IPEM\anaconda3\lib\site-packages\opcua\common\subscription.py", line 153, in _call_datachange self._handler.datachange_notification(data.node, item.Value.Value.Value, event_data) File "C:\Users\IPEM\Downloads\201124_SubscriptionHandler_bmb_v03.py", line 49, in datachange_notification df = {'Timestamp':Timestamp, 'GripperFlag': var.get_value()} #, columns=['Timestamp', 'GripperFlag']) File "C:\Users\IPEM\anaconda3\lib\site-packages\opcua\common\node.py", line 155, in get_value result = self.get_data_value() File "C:\Users\IPEM\anaconda3\lib\site-packages\opcua\common\node.py", line 164, in get_data_value return self.get_attribute(ua.AttributeIds.Value) File "C:\Users\IPEM\anaconda3\lib\site-packages\opcua\common\node.py", line 275, in get_attribute result = self.server.read(params) File "C:\Users\IPEM\anaconda3\lib\site-packages\opcua\client\ua_client.py", line 347, in read data = self._uasocket.send_request(request) File "C:\Users\IPEM\anaconda3\lib\site-packages\opcua\client\ua_client.py", line 83, in send_request data = future.result(self.timeout) File "C:\Users\IPEM\anaconda3\lib\concurrent\futures_base.py", line 441, in result raise TimeoutError() concurrent.futures._base.TimeoutError
I get this error regardless of if i implement get_value directly into the dataframe or define a variable "Gripperflag" which i then proceed to implement into the dataframe.
`def datachange_notification(self, node, val, data):
df_main = pd.DataFrame(columns=['Timestamp', 'GripperFlag'])
print("Python: New data change event", node, val)
# Gripperflag = var.get_value()
Timestamp = datetime.datetime.now()
print (Timestamp)
df = {'Timestamp':Timestamp, 'GripperFlag': var.get_value}
df_main = df_main.append(df, ignore_index=True)
print (df_main)`
If i set the variable Gripperflag outside of the datachange_notification definition, it keeps the very first value and doesn't use the updated one.
From what I remember, get_value
triggers a new read request. That is not needed, as the value for your monitored item has been forwarded to the callback already. Not sure any more if it's in val
, data
, or both (haven't followed the signature changes for the past few months), but the value after the change is definitely part of what gets dispatched on a data change event.
https://github.com/FreeOpcUa/python-opcua/blob/278a2b3673736d7aa9f00799c86d79e43d50bd5b/opcua/common/subscription.py#L18
as @starturtle mentiond val is the equivalent of the result of get_value()
keep in mind that you write the value with the data recieved timestamp not the source timestamp of the valuechange in your dataframe ;) (if you dont want that you should use the timestamp provided in the datavalue which is part of data)
https://github.com/FreeOpcUa/python-opcua/blob/278a2b3673736d7aa9f00799c86d79e43d50bd5b/opcua/common/subscription.py#L151
https://github.com/FreeOpcUa/python-opcua/blob/278a2b3673736d7aa9f00799c86d79e43d50bd5b/opcua/common/subscription.py#L61
item.Value.Value should be the datavalue if i remember right
Thank you both for your answers. It's so great to have such an active community, that's willing to help out newbies with our silly questions ;) I'll get back to you, as soon as i'm able to test this out.
I highly recommend you use a good IDE when dealing with OPC UA. Many of the structures are very complex and you need a debugger to view and understand them. For example your value might be in something like node.Value.Value.Varient
or some such thing.
Also, don't do long running code in the datachange callback because it blocks the network thread unfortunately.
Hello again. I've now managed to get it to work, as intended. It creates a Dataframe and feeds it with the timestamp from my local machine and the changed value. Unfortunately i'm unable to save the resulting dataframe from the datachange_notification.
`print("Python: New data change event", node, val)
global df_main Timestamp = datetime.datetime.now() print (Timestamp) df_main = df_main.append({'Timestamp':Timestamp, 'GripperFlag': val}, ignore_index=True) print (df_main) return (df_main) `
I can see a complete Dataframe within the console due to the print(), but i only have the starting value of Gripperflag in the dataframe accessible via the variable explorer.
Also, if i want to subscribe to a second node, do i need to give the SubHandler a list? Entering multiple variables into sub.subscribe_data_change() doesn't work. If so, how can i differentiate between them within the data_change function?
Or do i need a separate SubHandler for each subscribed node? That would make a globally accessible dataframe even more important, since the values should belong to the same timestamp.
Edit: Solved the first part by myself. My code never finished, therefore the dataframe was never propperly return
ed. I had commented out the client.disconnect
at the end of my code. The Question regarding multiple nodes to subscribe to still remains.
Also, if i want to subscribe to a second node, do i need to give the SubHandler a list?
Subscriptions for data changes with a list of nodes is possible. So you would pass something like [node1, node2, node3]
as first parameter.
Source: https://github.com/FreeOpcUa/python-opcua/blob/131ad8ceffeb7510b7bb3d9db1742896dbf74196/opcua/common/subscription.py#L190
Alright, i'm now subscribed to all nodes, i would like to have. Thanks for the help there!
While the subscription writes into my dataframe, is it possible for some other part of my script to access the dataframe? Currently i only get the finished dataframe after the connection closed. But for reinforcement learning i would need to oversee the changes in the dataframe. Also right now every value from the nodes is written in one column. Can i differentiate between the nodes and their values? As in
Timestamp1, ValueofNode1, ValueofNode2 Timestapm2, ValueofNode1, ValueofNode2
Right now it's written like this
Timestamp1, ValueofNode1 Timestamp1, ValueofNode2 Timestamp2, ValueofNode1 Timestamp2, ValueofNode2
the SubHandler class method "def datachange_notification(self, node, val, data):" provide the node which triggers the notification!
https://github.com/FreeOpcUa/python-opcua/blob/278a2b3673736d7aa9f00799c86d79e43d50bd5b/opcua/common/subscription.py#L18