Skip to main content

TCP Requests to communicate with local realtime applications.

Project description

B3DNet - TCP Server Requests

TCP setup for realime applications on local machines. As code can get executed based on client side requests, this implementation is not considered to be safe. Only execute Tasks from sources that you trust.
Build on top of socketserver and multiprocessing with the main goal to remote control blender via TCP.

Why not use Pickle?

The goal was, to give the ability to communicate from other languages.
In a nutshell, Tasks are jsons with a python function as string!

{
  "flag": 16,
  "idname": "UNIQUE_FUNCTION_ID",
  "func": """def some_fn(*args, **kwargs):\n\tprint("Hello world")""",
  "args": ["args", 21],
  "kwargs": {"hello": "world"}
}

Usage

Setup the threaded TCPServer to receive and execute tasks:

import queue
from b3dnet.connection import TCPServer, SERVER

q = queue.Queue()
server = TCPServer("localhost", 6000, q, b'')
server.connect(timeout=10.0)

# receive tasks
while server.flag & SERVER.CONNECTED:
    try:
        task = q.get(timeout=1.0, block=True)
    except queue.Empty:
        task = None
        break

    if task:
        q.task_done()

# flush queue
while not q.empty():
    task = q.get(timeout=QUEUE_TIMEOUT)
    if task is None:
        break
    task.execute()
    q.task_done()

Create and send tasks using a client:

import queue
from b3dnet.connection import TCPClient, CLIENT
from b3dnet.request import *
 
# connect the client
client = TCPClient("localhost", 6000, b'secret_key')
client.connect()


# function which should be passed
def hello_world(*args, **kwargs):
    print("Method from client which prints!", args, kwargs)

# register and call function
register_func = Task(
    (TASK.NEW_FN | TASK.CALL_FN), 'HELLO_WORLD_FN', hello_world
)
client.send(register_func)

# call the function using args
for i in range(0, 1000):
    call_data = Task(
        TASK.CALL_FN, 'HELLO_WORLD_FN', None,
        f"args_{i}", kwargs=f"kwargs_{i}")
    client.send(call_data)

# shutdown or restart the server request
client.send(Task((TASK.SHUTDOWN | TASK.CLEAR_CACHE), ))
# client.send(Task((TASK.RESTART)))

Lets extend this and expect we restarted the server:

# (re)connect the client
client = TCPClient("localhost", 6000, b'secret_key')
client.connect()

# create a list object
req = Task(TASK.NEW_OB, "OBJECT_ID", list)
client.send(req)

def fn_set(*args):
    return [i for i in list(args)]
 
# add args to the list using some fn 
# (fns and objs are in the same cache so naming matters)
args = [1, 2, 4, 3, 9, 6, 21]
req = Task(
    # it's possible to chain multiple tasks
    (TASK.NEW_FN | TASK.CALL_FN | TASK.DEL_FN | TASK.SET_OB), 
    ["FN_SET_ID", "OBJECT_ID"], fn_set,
    *args
)
client.send(req)

# some fn that takes args
def fn_ob_as_arg(*args):
    x = 0
    for arg in args:
        if isinstance(arg, int):
            x += arg
        if isinstance(arg, list):
            x += fn_ob_as_arg(*arg)
    return x

# send object as args (may use multiple objects)
req = Task(
    (TASK.NEW_FN | TASK.CALL_FN | TASK.OB_AS_ARG | TASK.DEL_FN),
    ["FN_SUM_ID", "OBJECT_ID", "OBJECT_ID"],
    fn_ob_as_arg, 10, 10, 10
)
client.send(req)

Task Concept

Using a dict to register functions and objs on the Listener side which then can be called from the Client side.
Once a function or ob isn't required anymore, consider to unregister it.
On the server side, functions may be registered when starting the application which can be called directly.
To do so, add functions to the CACHE dict.
If you do, consider to start the id name with 'PERSISTENT' so they don't get removed on restart or shutdown of the server.

Available flags:

TASK.NEW_FN             # Register a function with an unique idname
TASK.CALL_FN            # Call a function using its idname
TASK.DEL_FN             # Unregister a function

TASK.NEW_OB             # Register an ob
TASK.SET_OB             # Set an ob by a fn call
TASK.DEL_OB             # Unregister an ob
TASK.OB_AS_ARG:         # Use an ob as arg
TASK.OB_AS_KWARG:       # Use an ob with its idname as kwarg

TASK.PASSTHOUGH         # Do nothing (useful when using client on a seperate thread which pull from queue)
TASK.SHUTDOWN           # Shutdown the server and client
TASK.RESTART            # Restart the server and shutdown client

TASK.CLEAR_CACHE        # Clear all cached functions (also persistent ones)

Modules which can be used by default (others may get filtered out).
You can overwrite this on the server & client side if necessary. -> b3dnet.request.MODULES = [...]

'bpy', 'mathutils', 'bvhtree', 'bmesh', 'bpy_types', 'numpy',
'bpy_extras', 'bl_ui', 'bl_operators', 'bl_math', 'bisect', 'math'

Developer nodes

Everything is based around the Tasks.
Once started, the server waits for x-seconds for incoming connections and shutsdown if no connection has been established in time. Then the server basically servers forever in a separete thread, shutsdown and restarts if asked to. All Tasks the server receives are staged in a queue which may be accessed from another thread.
The Client is basically just a multiprocessing client which uses Task object.

# setup venv
python3 -m venv .venv
source .venv/bin/activate

# install optional requirements
pip install pytest
pip install mypi
pip install --upgrade build

# run tests
python3 -m pytest

# generate stubs if you make major changes
stubgen src/b3dnet

# build package
python3 -m build

License

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>.

Copyright (C) Denys Hsu - cgtinker, cgtinker.com, hello@cgtinker.com

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

b3dnet-0.0.1.tar.gz (19.3 kB view details)

Uploaded Source

Built Distribution

b3dnet-0.0.1-py3-none-any.whl (19.4 kB view details)

Uploaded Python 3

File details

Details for the file b3dnet-0.0.1.tar.gz.

File metadata

  • Download URL: b3dnet-0.0.1.tar.gz
  • Upload date:
  • Size: 19.3 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/4.0.2 CPython/3.9.6

File hashes

Hashes for b3dnet-0.0.1.tar.gz
Algorithm Hash digest
SHA256 32c9f3678c5663e8ba810d9693f32c2e37addc02fc54c20a51c2d0e312678e14
MD5 00ec5f0b26d8deabf1359b095728ffa4
BLAKE2b-256 401ff11d6dadd4cc0a3b8122f42e4fdbbf3f3f744baea103ef6d62c3fba79213

See more details on using hashes here.

File details

Details for the file b3dnet-0.0.1-py3-none-any.whl.

File metadata

  • Download URL: b3dnet-0.0.1-py3-none-any.whl
  • Upload date:
  • Size: 19.4 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/4.0.2 CPython/3.9.6

File hashes

Hashes for b3dnet-0.0.1-py3-none-any.whl
Algorithm Hash digest
SHA256 d9afb1143c95ab8832760c3ad4c544268ffbbae84a7ad07f83169c25a73f902e
MD5 f721c7af6a0a38e88e0e0ba06e821219
BLAKE2b-256 deed867a076327f14eef5d886ddc52e016d8eae04a06b6fd91a5abc7ae300801

See more details on using hashes here.

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page