Documentation of DSF Socket API



  • I plan on replacing a RAMPS board in a custom Machine i built. Right now i am sending all the GCODE over Serial from the python Program i#m running. I checked the Duet Web Control and saw it was using UNIX sockets to communicate with the DSF. Is there any Documentation on the API it exposes?



  • There isn't as of yet but check out this thread...
    https://forum.duet3d.com/topic/13556/q-for-dc42-and-chris-hammacher/22

    @Danal created a python module to talk over the unix socket.



  • If your Python is on a Pi on the network, you will want Websocket calls. The Websocket will be a tiny bit different from the DCS socket that I'm using in that other thread.

    For a Pi+Duet3 6HC with RRF3, the stack is something like:

    1. Browser on PC or Mobile
    2. <websocket-on-wifi-or-ether-network>
    3. DuetWebSocket on Pi
    4. <unix-socket-on-pi>
    5. DuetControlServer on Pi
    6. <spi-on-ribbon-cable>
    7. Firmware on Duet board

    You are going to want (2). I am using (4).

    I'd suggest getting an un-minified copy of DWC2 (from github), load that (file:// in the browser), open the developers console on your browser, and placing a stop on the actual websocket calls. That way you can see the payload, and figure out what parts you wish to use. It is all JSON, and therefore fairly easy to figure out.

    If you want to put your Python on the Pi that is ribbon cable attached to a Duet 3 6HC, then you can use exactly what I'm using in (4). See the other thread.



  • thanks to you guys. i will use the unix socket on the pi which is connected via the ribbon cable to the duet. the python socket example you showed in the other tread was exactly what i needed.



  • Call this "DWClib.py" and drop it in the directory where you intend to write your scripts:

    import socket
    import json
    
    def openDSF():
        s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
        s.connect('/var/run/dsf/dcs.sock')
        j=json.dumps({"mode":"command"}).encode()
        s.send(j)
        r=s.recv(128).decode()
        if (-1 == r.find('{"version":')):
              print("Failed to enter command mode - version not received")
              print(r)
              exit(8)
        if (-1 == r.find('{"success":true}')):   #could be in same buffer as version
            r=s.recv(128).decode()
            if (-1 == r.find('{"success":true}')):
                  print("Failed to enter command mode - success not received")
                  print(r)
                  exit(8)
        return(s)
    
    def closeDSF(s):
        s.close()
    
    def Gcode(s,cmd=''):
        print(cmd)
        j=json.dumps({"code": cmd,"channel": 0,"command": "SimpleCode"}).encode()
        s.send(j)
        r=s.recv(2048).decode()
        return(r)
    
    def getPos(s):
      result = json.loads(Gcode(s,'M408'))['result']
      pos = json.loads(result)['pos']
      print('getPos = '+str(pos))
      return pos
    

    And here is 'resetAxisLimits', which is not really an interface to the socket; instead, it is an example of how to re-run a specific subset of config.g. In this case, any line containing "M208". I have several others like this (not shown) because I'm overriding bits and pieces of config.g in other scripts, and I want to put them back.

    def resetAxisLimits(s):
      c = open('/opt/dsf/sd/sys/config.g','r')
      for each in [line for line in c if 'M208' in line]: Gcode(s,each)
      c.close()
    

    And a usage example. Call this anything you want, and invoke it via "./ScriptName.py":

    #!/usr/bin/env python3
    from DWClib import *
    
    DSFsock = openDSF()
    
    toolZ = [0,0]
    toolX = [0,0]
    toolY = [0,0]
    # Start of probing for Tool 0
    Gcode(DSFsock,'T0')                   # Pick up Tool zero
    Gcode(DSFsock,'G10 P0 Z0 X0 Y0')      # Remove all offsets from Tool zero
    Gcode(s,'M574 Z1 S1 P"!io4.in"')      # Set the nozzle wire as though it is an NO endstop
    Gcode(s,'G0 Z10       F1000')         # Lower bed to avoid collision with square hole plate.
    Gcode(s,'G0      Y225 F10000')        # Move nozzle to avoid other tools
    Gcode(s,'G0 X290      F10000')        # Move nozzle to avoid other tools
    Gcode(s,'G0 X290 Y295 F10000')   # Move nozzle to spot above flat part of plate
    Gcode(s,'G1 H3 Z1 F100')         # Probe until nozzle touches flat plate
    toolZ[tn] = getPos(s)[2]         # Capture the Z position at point of contact
    print(toolZ[tn])
    Gcode(s,'G0 Z10 F1000')          # Lower bed to avoid collision with square hole plate.
    Gcode(s,'M574 Z1 S1 P"nil"')     # Free the nozzle wire for the next axis
    
    ... similar code for X and Y, using M574 to probe...
    
    ... special code to reset endstops... 
    


  • @Danal to avoid confusion, you may not want to use the DWC acronym since that's just the GUI. Since the unix socket is created and served by the DuetControlServer, maybe use DCS instead.



  • @gtj0 said in Documentation of DSF Socket API:

    @Danal to avoid confusion, you may not want to use the DWC acronym since that's just the GUI. Since the unix socket is created and served by the DuetControlServer, maybe use DCS instead.

    Ahh... good catch. I'd started out thinking about the web, never went back and changed names. Same thing applies to DSFsock, etc.

    THANK YOU.


  • administrators

    There is a preliminary documentation of the DSF IPC API at https://github.com/chrishamm/DuetSoftwareFramework/tree/dev#api Once the latest DSF version is on the master branch (hopefully tomorrow), I will update the links again to point directly at the GH pages.



  • @chrishamm said in Documentation of DSF Socket API:

    There is a preliminary documentation of the DSF IPC API at https://github.com/chrishamm/DuetSoftwareFramework/tree/dev#api Once the latest DSF version is on the master branch (hopefully tomorrow), I will update the links again to point directly at the GH pages.

    Woo Hoo! Thanks Chris!


Log in to reply