Live gcode sending

edited January 19 in Repetier-Server
Hi, 

I am trying to send xyze coordinates from a custom python app to my repetier server. 
The idea is to perform "live plotting", which involves sending mouse coordinates to the server at a frequency of 60fps. So that's a ton of commands to process..

So I have read the Websocket API, and so far I can successfully send both 'send' actions and 'move' actions. I understood 'send' action is not designed to be queued like that, but it is working fine with the 'move' action. 

The problem I am facing is that it takes about 2 sec to process each new command, and I need it the to be the fastest possible. 

My code structure looks like this:
import subprocess
import json
import urllib.parse

####

coords = {"x":x, "y":y, "z":z, "e":e, "speed":speed} # new coords sent at 60fps

# format dict into json 
jsonparams = json.dumps(coords)
# encode json as a proper url
data = str(jsonparams).encode("utf-8")
data = urllib.parse.quote(data)
data = data.replace('%27', '%22')
# make the curl command
curl = f'curl.exe {host}/printer/api/{slug}?apikey={api}&a=move&data={data}'
# send command to server
subprocess.run(curl, capture_output=False, text=True)
Do you have any suggestion ? 

Any help would be very appreciated!


Edit: Well I think I found out... I am not using the right tool, and like so I am reopening a socket each time. So i need to use Websockets like so: ws://localhost:3344/socket/?lang=<Language ID>&sess=<session id>
I'll post a better answer later on!

Comments

  • That is true, wecksocket is much faster and also enforces correct order. Regular api would depend on which request is executed first and on multithreaded system like it is it can be wrong order on high speed.

    Reason you are so slow is you start an external program curl.exe instead of using a direct communication with python. Alway starting curl.exe as external process needs a lot of time.
  • edited January 21
    Hi !

    Thanks for the advice, I have been trying to implement websockets this week-end. It is a hard topic to me, I am making very slow progress... I got stuck on something, but I am not even sure I am going in the right direction. 

    So basically I need to send 60 'move' commands per second to my Repetier Server. 

    For now, I created the client side websocket responding to recv/send. But I am actually creating a websocket at each frame. Those have a timeout, but it is a mess anyway. 
    I didn't find a good solution to let the client websocket open for my stream of move commands.
    But is it what I should do ? 

    Here is the code I am using:
    class Communicate:
        def __init__(self, host, slug):
            self.url = f'ws://{host}/socket?apikey={self.api}'
            self.slug = slug
            self.callback_id = 100 #init
            self.th = None self.ws = None
        
        def _format_command(self, action="", data={}):
            d = {"action":action, "data":data, "printer":self.slug, "callback_id":self.callback_id}
            d = json.dumps(d)
            self.callback_id += 1
            return d
        
        async def _get_message(self):
            while True:
                try:
                    msg = await self.ws.recv()
                    msg = json.loads(msg)
                    print(f'\nrecv: {msg}')
                
                except websockets.exceptions.ConnectionClosed:
                    print('ConnectionClosed')
                    break

        async def _move(self, data):
            async def _send():
                await self.ws.send(data)
            async with websockets.connect(self.url) as self.ws:
                res = await asyncio.gather(self._get_message(), _send())
            return res

        def send_coord(self, data):
            """ :data: (json) {"x":x, "y":y, "z":z, "e":e, "speed":speed, "relative":relative} """
            
            def _do_it():
                r1, r2 = asyncio.run(self._move(data))
            
            data = self._format_command(data)
            self.th = threading.Thread(target=lambda: _do_it(), daemon=True)
            self.th.start()

    Thanks for your help :-)

    thasor
  • I'm no python programmer, but the idea of websockets making them so fast is to keep them open and just add new messages so there is nearly no delay.

    You should search the forum. I'm quite sure there was soneone presenting a free library to communicate with python. Or google it.
  • Hi !

    I made some noticeable progress on my project !

    First, the websocket server respond instantly to the given orders, which is very nice. I am now using the 'websocket' python module instead 'websockets' with the very basic threaded implementation. 

    My scenario is: 
    - I first open a single websocket for any communication with the printer
    - At mouse click and drag: a new record (list of 'move' actions) is started at 60 points a second (points are mouse coordinates)
    - At mouse released: the record is sent to the server websocket and (automatically) queued.

    I am updating the printer position on screen from the server 'move' response, so I know what the printer is doing. I am very happy with the result already, and drawing an other path when the first one is done works great. 

    But things are going messy when I start a new path while the server is still processing the last one. The commands seems to interpolate instead of being queued (I am using the same websocket for this though). 

    I tried something but I feel like I am not on the right track, because it makes it very complicated and laggy. 
    What I did is queueing the new records, and wait for the last one to be completed before sending the next. I simply compare the number of sent commands to the received ones, and trigger an event when equal. It makes a lot of new threads and a lot of loops.

    Do you think there could be an other solution, using the API ? 

    Thanks you for your help :-)
  • Please do not use "move" action. This is making a priority move added at first position of buffer where you are adding the moves. Please try using "send" action and send the correct gcode directly. This will be appended and no need to wait for a bunch to finish. Not sure if this causes the interference, but it might be the cause.
  • Oh, really ??? I understood the very opposite. Thanks a lot, I'm gonna try that.

  • Normally it should not make a difference. It is added as @moveAbs command and when it gets executed it puts the G1 command computed in front and deletes it self. You should see this in console when you enable commands viewing.
  • It's working like a charm with 'send' commands ! Thank you very much !
Sign In or Register to comment.