Welcome to Our Community

Some features disabled for guests. Register Today.

Reading values from OpenBuilds Control software

Discussion in 'Control Software' started by SeanD, May 18, 2020.

  1. SeanD

    SeanD Well-Known
    Builder

    Joined:
    Mar 23, 2019
    Messages:
    207
    Likes Received:
    65
    Is there an existing method to interfacing with the OpenBuilds Control software to read values into another application?

    I am wanting to extract information to extend functionality for a personal project.

    I have experience in web based technologies but not specifically the AppVeyor app builder. I am able to build a desktop app that simply reads the web server page on port 3000 at intervals which may be a good system without modifying my install of OpenBuilds Control - so that it can update down the track without issue.

    The main information I am seeking is: Is a job running (or detecting when it starts and stops).

    This would allow me to:

    1) start my time lapse on the camera that sits over my bed
    2) provide a costing based on run time
    3) send email notifications at the end of a job

    Also if possible I would like to read the max/min travel of X and Y axis. This would contribute to coatings in part 2 above.

    This is just so I can add some more enjoyment and expense calculations as a hobby. It isn’t meant for professional coatings.

    Happy to share anything I can make if it is possible.

    Does it sound like just scraping the data from the web service would be better?

    Thanks in advance.
     
  2. Peter Van Der Walt

    Peter Van Der Walt OpenBuilds Team
    Staff Member Moderator Builder Resident Builder

    Joined:
    Mar 1, 2017
    Messages:
    15,192
    Likes Received:
    4,346
    Appveyor/Travis is just used as CI compile tools (instead of manually compiling, uploading to git, and then creating a release - The CI services compiles a new release, for win, linux and mac, whenever a new git commit is done (we thus have to be mindful to update version number in package.json to not overwrite existing releases) and puts it on git, in a draft release ready for us to click "release" on - saves a lot of manual work :)


    Basically, EXCEPT its a little more modern, instead of a Poll/response type API, it uses Socket.IO Socket.IO to be fully event based. Socket.IO has plugins for node, python, any many more. What did you want to build your app on? (language)

    The difference is instead of polling "is the job still running" or "where in the queue are we" on a loop, with socket.io events you get an "event" sent from the backend, you just subscribe to the events in your app:

    For example
    (using js example as I have code onhand to copy paste)

    - CONTROL runs, and has a websocket server on 127.0.0.1:3000
    - You setup an app that imports the socket.io libraries and establish a connection to the Socket.io server

    Code:
    var socket = io(); // or  var socket = io('http://ip-address:3000);
    
    socket.on('status', function(status) {
       console.log(status)
    });
    
    All you need to get a full status report every 400ms and print it to the console

    The full status event on the event named "status" contains

    Code:
    var status = {
      driver: {
        version: require('./package').version,
        ipaddress: ip.address(),
        operatingsystem: false,
        powersettings: {
          usbselectiveAC: null,
          usbselectiveDC: null
        },
      },
      machine: {
        name: '',
        inputs: [],
        overrides: {
          feedOverride: 100, //
          spindleOverride: 100, //
          realFeed: 0, //
          realSpindle: 0 //
        },
        //
        tool: {
          nexttool: {
            number: 0,
            line: ""
          }
        },
        probe: {
          x: 0.00,
          y: 0.00,
          z: 0.00,
          state: -1,
          plate: 0.00,
          request: {}
        },
        position: {
          work: {
            x: 0,
            y: 0,
            z: 0,
            a: 0,
            e: 0
          },
          offset: {
            x: 0,
            y: 0,
            z: 0,
            a: 0,
            e: 0
          }
    
        },
        firmware: {
          type: "",
          version: "",
          date: "",
          buffer: [],
          features: [],
          blockBufferSize: "",
          rxBufferSize: "",
        },
      },
      comms: {
        connectionStatus: 0, //0 = not connected, 1 = opening, 2 = connected, 3 = playing, 4 = paused, 5 = alarm, 6 = firmware upgrade
        runStatus: "Pending", // 0 = init, 1 = idle, 2 = alarm, 3 = stop, 4 = run, etc?
        queue: 0,
        blocked: false,
        paused: false,
        controllerBuffer: 0, // Seems like you are tracking available buffer?  Maybe nice to have in frontend?
        interfaces: {
          ports: "",
          activePort: "" // or activeIP in the case of wifi/telnet?
        },
        alarm: ""
      }
    };
    
    Which covers most of the info you may want to react to

    Other cool events are

    Code:
    socket.on('grbl', function(data) {
        // emits true whenever a grbl startup string is found
    });
    
    Code:
    socket.on('data', function(data) {
        // data.command = command the response was in response to
        // data.response = responses from the backend (printed to serial log tab)
    });
    
    Code:
    socket.on('queueCount', function(queueCount) {
        // queueCount[0] == queueleft
        // queueCount[1] == queuetotal
        // useful for progress detection
    });
    
    Code:
    socket.on('jobComplete', function(data) {
        // data.completed = true
        // data.jobCompletedMessage = message set when sending the job (used in CONTROL to create a popup when things like Probe operatings completes)
    });
    
    Talking to the backend is very simlar, it subscribes to some events, and you just emit events into those listeners:

    for example, server listens for:
    Code:
    socket.on('runJob', function(object) {
        // debug_log(data)
        var data = object.data
        if (object.isJob) {
          uploadedgcode = data;
        }
    
        if (object.completedMsg) {
          jobCompletedMsg = object.completedMsg
    });
    
    So we can send it
    Code:
    socket.emit("runJob", {
        data = "G1 .... gcode block with \n linebreaks etc",
        isJob = true, // if isjob==true, it gets stored and can be retrieved when the UI restart/refreshes, use for actual jobs, for quick little tasks, set to false
        completedMsg = "String you want to print in control in a popup on completion"
    });
    
    Other events the server listens for, that may be of interest are:

    Code:
      socket.on('runCommand', function(data) { // data=single gcode/command });
      socket.on('feedOverride', function(data) { });
      socket.on('spindleOverride', function(data) { });
      socket.on('clearAlarm', function(data) { // Clear Alarm where data == 1 just clears the lockout, data == 2 clears the lockout, clears the queues, and resets all statusses back to the Stopped state});
      socket.on("connectTo", function(data) { // If a user picks a port to connect to, open a Node SerialPort Instance to it });
          console.log("Connecting via " + data[0] + " to " + data[1] + " at baud " + data[2]);
      });
      socket.on('closePort', function(data) { // Close machine port and dump queue });
    

    Watch for status.comms.connectionStatus in the "status" event:
    connectionStatus: 0, //0 = not connected, 1 = opening, 2 = connected, 3 = playing, 4 = paused, 5 = alarm, 6 = firmware upgrade
    So you'd start when it changes to "3", and stop anytime it changes to some other number


    Start counting time when it goes to "3" and stop when it goes to any other status then log time

    Watch for the socket.on('jobComplete', function(data) event
    BUT do not leave machine unattended! (; i can see what this email idea is for (;

    Code:
        socket.emit('runCommand', "$$")
    
    then listen for the output of the "data" event:

    Code:
      if (data.response.indexOf('$') === 0) {
            var key = data.response.split('=')[0];
            var param = data.response.split('=')[1]
    
            if (key == "$130") {
                // then param = x max travel
            }
            if (key == "$131") {
                // then param = y max travel
            }
            if (key == "$132") {
                // then param = z max travel
            }
        }
    
     
    #2 Peter Van Der Walt, May 18, 2020
    Last edited: May 18, 2020
  3. Peter Van Der Walt

    Peter Van Der Walt OpenBuilds Team
    Staff Member Moderator Builder Resident Builder

    Joined:
    Mar 1, 2017
    Messages:
    15,192
    Likes Received:
    4,346
    Oh and just to reply to this section: Yes, seperate app that would be better. Allows it to function separately. But do use socket.io - web scraper wouldn't work, the "web" section is static, and updated by JS based on socket.io events

    Though any features you need, do run it past OpenBuilds/OpenBuilds-CONTROL - if it truly benefits everyone it can be added as an official feature too
    (as long as it fits the big CONTROL mantras:
    - nothing in control required "configuration" unless it can't be helped, auto configured based on info from grbl settings, etc is a must
    - CONTROL is still the "beginner friendly" host and always will be, the focus is on ease of use, not all the bells and whistles imaginable)

    One warning though, we may change APIs at any time without notice (the ones above probably hasn't had any breaking changes in 2 years - but you never know) :)
     
    Safetywrench likes this.
  4. SeanD

    SeanD Well-Known
    Builder

    Joined:
    Mar 23, 2019
    Messages:
    207
    Likes Received:
    65
    Whichever way I go, I will add in checks so if a value isn’t obtainable the whole thing gives notice that something has changed.

    The code is amazing! Not brown nosing but I honestly haven’t seen code this clean outside of a couple of heavily developed frameworks before.

    Feel like a kid in a candy store. Will develop the stuff myself first as at least if I can demonstrate the benefit, if OpenBuilds later decides to implement anything then my work could just be concept testing.

    Not sure if I have said this yet but all of my stuff would be available to OpenBuilds and the community without hesitation.

    Will give this a good slug before coming back with too many questions so I don’t take up your time. I am very grateful.
     
    Mark Carew likes this.
  5. Peter Van Der Walt

    Peter Van Der Walt OpenBuilds Team
    Staff Member Moderator Builder Resident Builder

    Joined:
    Mar 1, 2017
    Messages:
    15,192
    Likes Received:
    4,346
    We'll take that as a compliment :)

    You get two kinds of open source devs: The ones that one to show off how many obscure one liners than can write, to score a job with, and the other kind that writes easy to read code, because we actually want our code to mean something to someone else (;
    In my past I made the mistake of partnering with the prior too - and lost control of some of my own projects. Nothing chases collaboration away faster than unfriendly and unreadable code.
     
  6. SeanD

    SeanD Well-Known
    Builder

    Joined:
    Mar 23, 2019
    Messages:
    207
    Likes Received:
    65
    Update on the html scraping technique

    I was really excited as it seemed quite easy to pull say the Connection Status by using HTML Agility Pack to read 127.0.0.1:3000 and look for the id "connecStatus". I forgot that each client instance is treated individually, so when a gcode file is loaded in the main browser windows, this doesn't allow my app to read that data. This means I will have to look at other options.
     
  7. Peter Van Der Walt

    Peter Van Der Walt OpenBuilds Team
    Staff Member Moderator Builder Resident Builder

    Joined:
    Mar 1, 2017
    Messages:
    15,192
    Likes Received:
    4,346
    (; as per
    Actually, an http get to 127.0.0.1:3000/gcode will get you gcode while a job is running
     
  8. SeanD

    SeanD Well-Known
    Builder

    Joined:
    Mar 23, 2019
    Messages:
    207
    Likes Received:
    65
    Thanks mate, sorry for you having to repeat that - it just took me doing it myself to fully comprehend your statement. I will try and ensure I consider ALL of the information you have provided moving forward so I don't take up any of your time unnecessarily.
     
    Peter Van Der Walt likes this.
  9. SeanD

    SeanD Well-Known
    Builder

    Joined:
    Mar 23, 2019
    Messages:
    207
    Likes Received:
    65
    So basically I have worked on creating a job history tracker. The extra features such as email notifications and costing will be the next step but thanks to the awesome help on here I can now find out when a job was run, record how long it took and the approx material used. Will send through some screenshots later this week.
     
    Peter Van Der Walt likes this.
  10. SeanD

    SeanD Well-Known
    Builder

    Joined:
    Mar 23, 2019
    Messages:
    207
    Likes Received:
    65
    I am getting the status update between 10 and 128 times a second sometime. Using the socket.on status - is this normal? I figured from the reply it should only be coming through once every 400ms.
     
  11. Peter Van Der Walt

    Peter Van Der Walt OpenBuilds Team
    Staff Member Moderator Builder Resident Builder

    Joined:
    Mar 1, 2017
    Messages:
    15,192
    Likes Received:
    4,346
    At least once every 400ms but there are other events that emit status: search index.js for
    Code:
    .emit("status
    and maybe single quote too
    Code:
    .emit('status
     

Share This Page

  1. This site uses cookies to help personalise content, tailor your experience and to keep you logged in if you register.
    By continuing to use this site, you are consenting to our use of cookies.
    Dismiss Notice