tcp streaming setup

Hi,

I try to connect some external hardware to Blender via tcp. I could connect to it and it gives back the values I request.
Now I want to repeat this request every second, or even more often, to kind of stream the values. Here is what I did until now:

import socket
import struct
 
# values
ip = "192.168.1.11"
port = 51955
command = "S01"
cs = "80"
 
# create socket
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
 
# bind socket
s.connect((ip,port))
 
# pack data
data = struct.pack("bsssssb",0x01,command[0],command[1],command[2],cs[0],cs[1],0x17)
 
# send command
s.send(data)
 
# get answer
ack = s.recv(1024)
speed = s.recv(1024)
 
# close socket
s.close()
 
print "speed: ",float(speed[4:len(speed)-3])*3.6,"km/h"

After sending the command to the hardware, I get 2 packets: the first one (“ack”) got only a ACK flag, the second (“speed”) the value I want.

Right now, the script runs with an “Always”-sensor (pulse OFF) only once. And it’s all fine. But how can I get this to “stream” the values, meaning repeating the request more often?
When I activate pulsing on the Always sensor, the packets on the network seem to mess up somehow, only one packet arrives and the script stops, waiting for the second.

Is there a way to connect the socket “global”, keep it open, and only send the command and recieve the result each frame, instead of doing the whole process (open -> send -> recieve -> close) each frame?

I thought about something like threading maybe, but could not get anything working.

Would be great if someone could give me a hint, how to handle such a tcp connection best. :slight_smile:

Greetings,
Quen

edit:
if it’s of note: this is what happens on the network when I run the script:
http://img824.imageshack.us/img824/4285/myconnection01.jpg
packet 6 is the send() command, packet 8 is the first ACK and packet 9 is the answer containing the value

Yes, there’s a way to do that.
The simple way is the following one.
You must place the socket in the GameLogic object.
This is UDP, but should work also for TCP (but you must modify the flow of the calls, add connect and listen…)


import Blender
import GameLogic as G
import socket
from cPickle import *

def init():
        # create UDP socket for sending datagram
        G.sender = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        G.sender.settimeout(0.01)
        G.sender.setblocking(0)
        # creates UDP socket for receiving datagram
        G.listener = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        G.listener.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        G.listener.settimeout(0.01)
        G.listener.setblocking(0)
        G.listener.bind((G.ownIP, G.oPort))

def send(x):
	# Send data
	try:
		G.sender.sendto(x, (G.peerIP,G.oPort))
		#print "[I] Netcode sent data: "
		#print loads(x)
	except:
		#print "[E] Netcode send failed"
		Nothing = 0

def recv():
	# Receive data
	try:
		data, (G.ownIP, G.pPort) = G.listener.recvfrom(G.buffer)
		#print "[I] Netcode received data: "
		#print loads(data)
		return loads(data)
	except:
		#print "[E] Netcode receive failed"
		Nothing = 0
	return None

I placed a property in each object which had to be “dynamic”.
If this property is set, I sent info of the object using serialization (cPickle, 100 times faster than Pickle).

The other way is to add a thread in Blender code and re-compile it. You’ll have a deeper control.

Threading can be done using multiple objects having the recv code. If order of messages is important, you must queue them in an ordered way (use a counter).
For more info, feel free to contact me.
Hope it helps,

Luca

Woh, looks good! I guess, I will have to look deeper into that :slight_smile:
Thank you for your answer, Luca!
I will try around with that and let you know how it works.

You’re script only needs 3 changes:

  1. Store the socket so it can be used between runs of the script
  2. Set the socket to be non blocking (the same as having a time out of 0) so you don’t stall the engine
  3. Wrap the recv calls in a try/except to handle errors from the nonblocking socket.

Here is the revised script (untested):


import socket
import struct

import GameLogic as gl
 
# values
ip = "192.168.1.11"
port = 51955
command = "S01"
cs = "80"
 
# create socket and store it in GameLogic
if not hasattr(gl, "s"):
	gl.s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
	 
	# bind socket
	gl.s.connect((ip,port))
	
	# set the socket to be nonblocking so we don't freeze the engine
	gl.s.setblocking(0)
	# setting a time out of 0 is the same as a non blocking socket
	# gl.s.settimeout(0)
 
# pack data
data = struct.pack("bsssssb",0x01,command[0],command[1],command[2],cs[0],cs[1],0x17)
 
# send command
s.send(data)
 
# get answer
try:
	ack = s.recv(1024)
	speed = s.recv(1024)
except socket.error:
	# If you don't get any data from recv, you'll get an error becasue the socket is 
	# set to non blocking (something about non blocking call not being able to complete)
	pass
 
# close socket
s.close()
 
print "speed: ",float(speed[4:len(speed)-3])*3.6,"km/h"

Moguri, thanks for your answer, too.

I got it working with a combination of both solutions above :slight_smile:

I have one script opening the socket (if it isn’t open already) and sending the command, and another one that receives all the time. So far so good.

But for any reason, the hardware I connected to Blender keeps sending the answer of my request every few seconds. As you can see in the attachment, packet no. 9 is the one I wanted. The socket sends back an ACK, all fine.
But 11 seconds later the same value is sent from the hardware again (packet no. 11). And this keeps on als long as the engine runs (packet no. 13).
The later packets (11&13) got different sequenz numbers, so the hardware is not thinking that it’s packet got lost and just resends it. It’s a new event. But the value stays always the same, also I can see on the display of the hardware that it changed in the meantime.

Why I am posting this here at all is because I would like to know if there is something special about receiving packets from a tcp connection? Maybe some special parameters or flags that should be set in the socket options?
(I just want to believe that this can be fixed without going into that hardware :D)

If anybody got an idea, just tell me! I would try everything :wink:
Thanks :slight_smile:

Attachments


I would first try getting it working outside of the engine with a small console app. This way you eliminate one more thing that could be messing things up. In the console app though, I would try using non-blocking sockets simply because that is what you’ll need in the engine.

Once you get the test app working, you can port it to the engine.

I hope that helps.

Cheers,
Moguri

Yes, that would have been the way to go, if i wouldn’t have found this:
The firmware on the hardware was waiting for an ACK, too! (not the automated default tcp ACK) I had to back the ACK as data (“0x06”) into a paket and send it to the bike again, after receiving a paket.
Who could have known that? :eek:

That was all, problem solved. :slight_smile:

Thank you anyways for your hints about setting up the socket scripts, that really helped me a lot :slight_smile: