UDP data to C4d via Python



  • On 05/03/2013 at 00:26, xxxxxxxx wrote:

    Hi,
    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)
    sock.bind(ADDR)

    while True:
           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.

    Best,
    -Niklas



  • On 05/03/2013 at 03:48, xxxxxxxx wrote:

    OK, i have some experience with java, javascript and python. 
    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.

    -Niklas



  • 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:

    Hi Niklas,
    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:

    Hi Helge,
    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.

    -Niklas



  • On 07/03/2013 at 05:57, xxxxxxxx wrote:

    ok, that is my actual code, connected to a null object.

    import c4d
    import socket
    import threading

    class ServerThread(threading.Thread) :

    def __init__(self, host='127.0.0.1', 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!
            # 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
            while self.running:
                data, addr = sock.recvfrom(self.bufsize)
                self.on_data_recv(data, addr)

    thread = ServerThread()
    thread.start()

    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:

    Hi Helge,

    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.

    -Niklas



  • On 07/03/2013 at 08:53, xxxxxxxx wrote:

    ok niklas,
    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.
    thanks, helge



  • On 07/03/2013 at 09:36, xxxxxxxx wrote:

    Hi Helge,

    just to clarify: The example above was intented to be used in a Python Scripting Tag.

    Best,
    -Niklas



  • 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


Log in to reply