Open AndrewStuhr opened 4 years ago
what do you mean with failing is it a python error or a opcua statuscode?
It's an opcua status code : bad 0x80000000.
It takes about 3-4 seconds to from when the read request begins until opcua generates this statuscode. I'm looking for a way to read multiple tags every second and get a bad quality response just for bad quality tags, yet still get tag values for good quality tags. Any thoughts?
Thanks again!
sounds impossible... one read request and multiple responses... if it would be an python server i would use a opcua-method or if the datatypes are equal just a variable with n dimensions... how many variables u plan to read?
I worded that poorly... I'm looking to get one response back with an array/object that contains read values for each node. However, if a node had bad quality, the array/object would have some indication of the bad quality instead of the node's value.
Currently I was just reading a couple of nodes, but eventually I'm hoping to read a lot - around 1000 per second.
ive read 2048 boolean io from a s7-1500 but it was only possible as an array (variable type boolean with arraydimension of 2048)!
but to be honest with you ive never worked with kepware ive wrote the services by myself...
what i wonder atm is why a node could have bad quality?
In this case, it's because Kepware is reading data from scale that may or may not have an object on it. If there isn't an object on the scale, it responds to Kepware in a way that makes the Kepware quality go bad. Once an item is being weighed, the quality in Kepware temporarily becomes good.
Do you think the current method I'm using to read multiple variables will be unable to handle 1000 nodes, even with all good quality?
the thing is, it depends on the server hardware, network traffic, and a bunch of more things like opcua server config... u can read 1000 nodes for sure but in 1s interval...
python-opcua/opcua/client/ua_client.py:
def read(self, parameters):
self.logger.info("read")
request = ua.ReadRequest()
request.Parameters = parameters
data = self._uasocket.send_request(request)
response = struct_from_binary(ua.ReadResponse, data)
self.logger.debug(response)
response.ResponseHeader.ServiceResult.check()
# cast to Enum attributes that need to
for idx, rv in enumerate(parameters.NodesToRead):
if rv.AttributeId == ua.AttributeIds.NodeClass:
dv = response.Results[idx]
if dv.StatusCode.is_good():
dv.Value.Value = ua.NodeClass(dv.Value.Value)
elif rv.AttributeId == ua.AttributeIds.ValueRank:
dv = response.Results[idx]
if dv.StatusCode.is_good() and dv.Value.Value in (-3, -2, -1, 0, 1, 2, 3, 4):
dv.Value.Value = ua.ValueRank(dv.Value.Value)
return response.Results
i assume your code looks like this:
for node in nodeList:
nodeid = ua.NodeId.from_string(node)
attr = ua.ReadValueId()
attr.NodeId = nodeid
attr.AttributeId = ua.AttributeIds.Value
params.NodesToRead.append(attr)
result = client.uaclient.read(params)
if the nodelist does not change at runtime: the first thing i would try is to run the forloop just once an store "params", so you gain some speed (1000 nodes => 1000 iterations to get params + 1000 iterations in the for loop of the read-method => 2000 iteration total ...) the next thing is to try multiprocessing/multithreading with more then one opcua-client-connection so u can split up work depends on your hardware...
but all that does not help with the bad quality in kepware... :(
Thanks for the performance tips. I'll try these out.
I was thinking I could modify the opcua library to handle the bad status code differently, but I don't think that would even help because it take's 3-4 seconds to just get the response. Let me know if you have any other thoughts come to mind! Thanks
This sounds like a bug. I do not think an entire read request should fail due to one node having bad status.
However, it will be many times faster to subscribe to 1000 items than do an explicit read. Not to mention subscribing to 1000 nodes will solve your bad quality issue because one bad node won't stop the subscription from updating all the other ones in the subscription.
Damn, I haven't even looked into subscriptions. I've just been reading on an interval. I'll definitely try subscriptions, thank!
@zerox1212: is the subscription on servers with encryption still broken or fixed? (OPC UA Server: AttributeError: 'NoneType' object has no attribute 'encrypted_block_size' #956)
I don't know. I never used encryption when I used this library.
from opcua import Client
nodeList = ["ns=0;i=2267","ns=2;i=2"]
class SubHandler(object):
"""
Client to subscription. It will receive events from server
"""
def datachange_notification(self, node, val, data):
print("Python: New data change event", node, val)
def event_notification(self, event):
print("Python: New event", event)
if __name__ == "__main__":
client = Client("opc.tcp://127.0.0.1:4840")
client.connect()
handler = SubHandler()
sub = client.create_subscription(500, handler)
for node in nodeList:
handleList = sub.subscribe_data_change(client.get_node(node))
Thanks for the code Andreas! I'm able to set up a subscription, and it's handling bad quality tags (it just returns 'None').
One final question: I'm running your script in Window's command prompt. Is there a way to exit the script? Currently, CTL + C isn't doing anything. Is there something I have to do with client.disconnect()?
Thanks again
try: while 1: time.sleep(0.5) except KeyboardInterrupt: unsubscribe loop disconnect
sorry wrote it on my phone...
I used the following code and now I can stop the script with CTL + C - thanks.
The only issue is I get this duplicate output message: "ServiceFault from server received while waiting for publish response" (twice)
I believe these messages are print statements and not errors because there's no traceback, literally just this text. Do you see anything I should change to prevent these messages?
if __name__ == "__main__":
client = Client("opc.tcp://localhost:49320")
client.connect()
handler = SubHandler()
sub = client.create_subscription(500, handler)
handleList = []
for node in nodeList:
handleList.append(sub.subscribe_data_change(client.get_node(node)))
try:
while 1:
time.sleep(0.5)
except KeyboardInterrupt:
for handle in handleList:
sub.unsubscribe(handle)
sub.delete()
client.disconnect()
I actually had the same issue with the double printed it appearce that there is a bug... but not with pub/sub i got it with opcua-method-call... till now i could not find anything...
Interesting... please let me know if you figure it out. And thanks again, you rock man!
I'm trying to read multiple tags from Kepware in a single request. If one or more tags has bad quality, the entire read fails. Is there a way to get a bad quality status back for bad tags without the entire read failing? Here's part of my code where I'm reading multiple tags at once:
for node in nodeList: nodeid = ua.NodeId.from_string(node) attr = ua.ReadValueId() attr.NodeId = nodeid attr.AttributeId = ua.AttributeIds.Value params.NodesToRead.append(attr) result = client.uaclient.read(params)
Thanks