UDP data to C4d via Python
On 05/03/2013 at 00:26, xxxxxxxx wrote:
i am new to C4d but i like the Python implementation. I want to control some C4d functions like scale or rotation from MaxMSP via UDP Network.
I know that this is possible to send Data to C4d because of the KiCapOSC. But my first tryout with UDP receive didn't work. Simple Code Example to show the data in the console :
from socket import *
HOST = '192.168.178.21'
PORT = 20000
BUFSIZ = 1024
ADDR = (HOST, PORT)
sock = socket(AF_INET, SOCK_DGRAM)
data, addr = sock.recvfrom(1024)
print 'data:', data
can anybody give me an advice where to start?
On 05/03/2013 at 02:27, xxxxxxxx wrote:
Well, your example code seems to be correct at first glance. What's the exact error/problem?
On 05/03/2013 at 02:46, xxxxxxxx wrote:
yeah compiling works, but if i run the code c4d freeze.
On 05/03/2013 at 03:29, xxxxxxxx wrote:
Because the code is run from the main thread and you never leave the while loop. Do
you have programming experience? If you don't already know threads and (fake) parallel processing, you should inform yourself,
learn about it and then implement your code in a thread. There is enough online ressource to get started.
Be told that the script manager is not the optimal place to start threads, you'll be better off in a CommandData plugin.
On 05/03/2013 at 03:48, xxxxxxxx wrote:
I don't have much experience with thread programming. So i will check CommandData(is this controlling from the command line?)
But one short question, if i disable the while loop and check each running frame the input, this should work?
On 05/03/2013 at 04:10, xxxxxxxx wrote:
No, the CommandData plugin is an interface to run your custom code when the user starts your
command via the plugin-menu. All available commands can be found in the Command Manager. An
example command would be "Save" or "Open", "Optimize" or "Untriangulate".
You could, for example, create a Python Tag that starts the socket once and checks for input data
on each frame, depending on what you want to achieve. You also have to think about the case
when the timeline is not running: Still check for input data from clients, and if so, what to do
with this data?
When implementing threading, you should also include the ability to stop the thread when the user
does not want to use your plugin anymore. You should also ensure you don't hit any cross-thread
barriers (like modifieng the scene from a thread is not allowed).
Here's an example:
import socket import threading class ServerThread(threading.Thread) : def __init__(self, host='192.168.178.21', port=20000, bufsize=1024) : super(ClientThread, self).__init__() self.sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) self.sock.bind((host, port)) self.bufsize = int(bufsize) self.running = False def on_data_recv(self, data, addr) : # Do some stuff with your data. Remember you're still running # in a thread! pass # Overrides: threading.Thread def run(self) : self.running = True while self.running: data, addr = sock.recvfrom(self.bufsize) self.on_data_recv(data, addr) thread = ServerThread() thread.start()
In Cinema 4D, you might also use the c4d.threading.C4DThread class.
On 05/03/2013 at 04:52, xxxxxxxx wrote:
Thank you Niklas for your efforts, i will try this in the evening when i am at home.
I thought it is easy to create a null object and put a little script to to change the position or rotation, but it looks more complicated ;)
On 07/03/2013 at 02:01, xxxxxxxx wrote:
I didn't get it working :(. I tried to connect the script to a null object and check each frame if it is getting data from the udp then change the x position and go to the next frame. if there is no data go to the next frame without changing. i think that is a thread problem, you pointed on. perhaps you can give me a short example. thanks
On 07/03/2013 at 03:47, xxxxxxxx wrote:
what exactly is the problem? Did an exception occure? Or did Cinema freeze again? Show also the code, please. I can't help you unless I know your actual problem.
On 07/03/2013 at 05:57, xxxxxxxx wrote:
ok, that is my actual code, connected to a null object.
class ServerThread(threading.Thread) :
def __init__(self, host='127.0.0.1', port=20000, bufsize=1024) :
self.sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
self.bufsize = int(bufsize)
self.running = False
def on_data_recv(self, data, addr) :
# Do some stuff with your data. Remember you're still running
# in a thread!
# c4d should check each frame if it is getting data
# if not go to the next frame, if data then change the x position
# and go to the next frame
[c4d.ID_BASEOBJECT_REL_POSITION,c4d.VECTOR_X] = data
# Overrides: threading.Thread
def run(self) :
self.running = True
data, addr = sock.recvfrom(self.bufsize)
thread = ServerThread()
time = doc.GetTime().GetFrame(doc.GetFps())
i think my problem is in connection of time and thread. i am really new to python in c4d.
On 07/03/2013 at 06:21, xxxxxxxx wrote:
[c4d.ID_BASEOBJECT_REL_POSITION,c4d.VECTOR_X] = data
the instance for which __setitem__ should be called is missing, the code doesn't make much
sense this way.
On 07/03/2013 at 06:32, xxxxxxxx wrote:
threading is recommended for plugins only. In order for the thread to stay alive, you require to
keep a reference to it, which can be garuanteed in a plugin, but not necessarily in a Python Tag
or any built-in scripting facility in Cinema (script, effector, object, tag). If the reference to the thread
is lost, it will be garbage collected and therefore stopped and eliminated.
Please encapsulate your code in the [*CODE][*/CODE] tags next time, sparing the * of course.
A Python Tag's main() function is called everytime the tag requires to be executed, to be more
precise: Everytime TagData.Execute() is invoked from Cinema 4D's plugin system, main() is invoked.
You do not have a main() function declared, therefore no main() function can be invoked.
And yes, you are again hitting threading-limitations. When you put your code into an expression
tag, you should do this because of a certain reason. What you are currently doing could also be put
into a command plugin, an object plugin or even video post plugin, because the code is totally
unrelated to its environment.
EDIT: I missed, what Ferdinand pointed out. :) So you're not hitting threading limitations here,
_but if you would've applied this part correctly (with an object to invoke _setitem__ with [bracket
syntax], and convert the data to a numeric type) you wouldn't have seen the changes immediately.
Here's some example code you should use to kick off:
import c4d import socket sock = None HOST = 'localhost' PORT = 20000 bufsize = 1024 timeout = 0.01 def main() : global sock if not sock: # No socket has yet been created. sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) sock.bind((HOST, PORT)) # You don't want the receiving of data to block the complete # evaluation process. It's bad when no data comes in, but it # needs to be handled. sock.settimeout(timeout) try: data, addr = sock.recvfrom(bufsize) except socket.timeout: # No data has been received within the timeout. Need to handle # this here!! print "timeout reached" return # `data` is a string with up to `bufsize` bytes, what to do with it? pass
This code will check each frame for incoming data! Your current code is completely independant
from the actual frames in Cinema. Remember that the data you recieve from the socket is a string
of up to 1024 bytes (can be less!), and not an integral or floating-point number. You can't assign
a string to a component of a vector like you're doing in your current code. You need to convert it.
On 07/03/2013 at 08:53, xxxxxxxx wrote:
i think i workout the command plugin tutorial on smart-page.net the next days. also i will study threading of a better understanding. after this i will try to connect your example.
On 07/03/2013 at 09:36, xxxxxxxx wrote:
just to clarify: The example above was intented to be used in a Python Scripting Tag.
On 07/03/2013 at 10:31, xxxxxxxx wrote:
Yepp i know.
I tested your script and yeah it works, i changed the pass arg ti print data to see the data in the console. If i go frame by frame it works fine, expect that the received message is a little bit strange, i create a counter fom zero to ten with delay of one second in max map and send it to c4d. If i skip the frame faster or longer then one second, c4d counts from 0 to 10.
furthermore, c4d crashed if i put the play button. i think both are threading problems so i think it is better to do it with an command line plugin and create the null object by the plugin.
i also have to create an socket close like you mentioned. So I'm checking the tutorials and the i am back ;)
greetings - helge