UDP driver altering data

I’ve encountered a problem with the UDP driver. I’m trying to bring in a sequence of bytes which contain binary representations of different values (e.g. 32-bit integers and 32-bit IEEE floating point numbers). I’ve set the Message Delimiter Type to PacketBased, which brings the whole sequence in as a string.

The problem with this is that it cannot cope with byte values greater than 127. If it finds any it replaces them with 0xfd.

Any ideas how we can access the correct values? We were hoping to use the Python struct library in a tag’s Value Changed event to parse the values and write them out to other Memory tags.

I don’t think these messages can survive a round trip from binary -> unicode -> binary again. Or, at least, I haven’t figured out a way to get the original binary out of a unicode string yet.

The UDP driver message tag may not be the appropriate conduit for your data. Maybe you can do the UDP part in scripting as well.

Do you mean some kind of Gateway script, or something lower level than that?

Yeah, maybe start something up in a gateway startup script that runs until shutdown. I think simple socket communication is easy enough to do in Python/Jython that you should try to avoid the overhead involved with writing a module.

I tried the following script in the Gateway startup script:[code]import socket
import binascii

UDP_IP = “192.168.1.250”
UDP_PORT = 5005

sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock.bind((UDP_IP, UDP_PORT))

while True:
data, addr = sock.recvfrom(1024)
print “Received %s from %s” % (binascii.hexlify(data), addr)
system.tag.write("[tags]udp_byte", data)[/code]but my Designer now hangs when I try to save it. I’d imagine the Gateway is now thrashing non-stop at the script - is there any way of avoiding this and still running quickly enough to read each packet as it arrives?

You’ll probably want to throw all that into a function and then invokeAsync that function.

Nearly! The script is now

def read_udp():
	import socket
	import binascii

	UDP_IP = "192.168.1.250"
	UDP_PORT = 5005

	sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
	sock.bind((UDP_IP, UDP_PORT))

	while True:
		data, addr = sock.recvfrom(1024)
		print "Received %s from %s" % (binascii.hexlify(data), addr)
		system.tag.write("[default]udp_byte", data)

system.util.invokeAsynchronous(read_udp)

It gets the data, but then gives the following error:

INFO   | jvm 1    | 2015/08/11 21:07:04 | Received 01 from (u'192.168.1.1', 51771)
INFO   | jvm 1    | 2015/08/11 21:07:04 | ERROR [GatewaySystemUtilities        ] [21:07:04,005]: Error running function from system.util.invokeAsynchronous
INFO   | jvm 1    | 2015/08/11 21:07:04 | Traceback (most recent call last):
INFO   | jvm 1    | 2015/08/11 21:07:04 |   File "<[test] Startup Script>", line 14, in read_udp
INFO   | jvm 1    | 2015/08/11 21:07:04 | NameError: global name 'system' is not defined
INFO   | jvm 1    | 2015/08/11 21:07:04 | 
INFO   | jvm 1    | 2015/08/11 21:07:04 |       at org.python.core.Py.NameError(Py.java:260)
INFO   | jvm 1    | 2015/08/11 21:07:04 |       at org.python.core.PyFrame.getglobal(PyFrame.java:265)
INFO   | jvm 1    | 2015/08/11 21:07:04 |       at org.python.pycode._pyx2.read_udp$1(<[test] Startup Script>:11)
INFO   | jvm 1    | 2015/08/11 21:07:04 |       at org.python.pycode._pyx2.call_function(<[test] Startup Script>)
INFO   | jvm 1    | 2015/08/11 21:07:04 |       at org.python.core.PyTableCode.call(PyTableCode.java:165)
INFO   | jvm 1    | 2015/08/11 21:07:04 |       at org.python.core.PyBaseCode.call(PyBaseCode.java:301)
INFO   | jvm 1    | 2015/08/11 21:07:04 |       at org.python.core.PyFunction.function___call__(PyFunction.java:376)
INFO   | jvm 1    | 2015/08/11 21:07:04 |       at org.python.core.PyFunction.__call__(PyFunction.java:371)
INFO   | jvm 1    | 2015/08/11 21:07:04 |       at org.python.core.PyFunction.__call__(PyFunction.java:361)
INFO   | jvm 1    | 2015/08/11 21:07:04 |       at org.python.core.PyFunction.__call__(PyFunction.java:356)
INFO   | jvm 1    | 2015/08/11 21:07:04 |       at com.inductiveautomation.ignition.common.script.ScriptManager.runFunction(ScriptManager.java:637)
INFO   | jvm 1    | 2015/08/11 21:07:04 |       at com.inductiveautomation.ignition.gateway.script.GatewaySystemUtilities$1.run(GatewaySystemUtilities.java:65)
INFO   | jvm 1    | 2015/08/11 21:07:04 |       at java.lang.Thread.run(Thread.java:745)

The scoping has changed a bit for 7.7. Try importing system as well.

I saw that just as you posted Jordan :slight_smile: I’m now getting an ‘Address already in use’ error. It doesn’t seem to be closing the socket before trying to open another one the next time I save the project.

Was afraid of this…

You’re going to need to shutdown the socket you create, in a shutdown script perhaps?

But then you’ll need to be able to get ahold of the socket… maybe you can store it as a global singleton? Seems there was just a thread about that kind of thing.

For anyone else testing this, my Python script for sending data to the Gateway from the command line is as follows:

import socket
import struct

UDP_IP = "192.168.1.250"
UDP_PORT = 5005

sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) 
MESSAGE = struct.pack("B", 0x01)
sock.sendto(MESSAGE, (UDP_IP, UDP_PORT))

Note that both the sending script and the receiving script use the destination IP address, which is a bit counter intuitive.

Anyone from IA able to help with this one? Kevin’s suggestion about storing the socket as a global variable so it can be closed properly sounds good but is beyond my ability :slight_smile:

Hi Al,

I just finished implementing and testing controlled, organized, supported global storage in Ignition.

It is called “gstore”, short for Global Storage. It provides a simple way to have and use global state in Ignition.

Global project state is stored in a global dictionary at pa.gstore. pa.gstore is global to each project. It can be used in Gateway scripts or client/designer scripts.

Here’s the code to use in your circumstance:

Put this code in a Gateway Startup Script:[code]def read_udp():
import socket
import binascii

UDP_IP = “192.168.1.250”
UDP_PORT = 5005

sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
pa.gstore[“sock”] = sock
sock.bind((UDP_IP, UDP_PORT))

while True:
data, addr = sock.recvfrom(1024)
print “Received %s from %s” % (binascii.hexlify(data), addr)
system.tag.write("[default]udp_byte", data)

system.util.invokeAsynchronous(read_udp)[/code]
The only difference to your code is adding the socket to gstore.

Then add the following code to the Gateway Shutdown Script:pa.gstore["sock"].close()gstore has more functionality than this but I haven’t updated the documentation yet, but I will soon. If you want to use this and need any help let me know.

gstore is the newest capability in the PA Power Scripting Module.

Edit: getting rid of GStore: nickmudge.info/post/getting-rid-of-gstore
Use Python module level variables instead: nickmudge.info/post/python-modul … lient-tags

Best,

I tried testing it out. I’m having problems with reconnecting the socket once it is closed. The OS doesn’t want to use a port for awhile once it is closed.

I found that using this code before binding and not closing the socket seems to work:

sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)

Thanks Nick, that seems to have sorted in out. It took a bit to get going - I had to change to another port for a while before going back to the original port - so I’m not sure yet how reliable it is, but this lets me keep testing.

7 posts were split to a new topic: Closing a TCP Socket from Jython