Controlling robots from inside AutoCAD – Part 2

After looking at how to control robots using Cylon.js in the last post, in this post we're going to get that working inside AutoCAD. For now with just a command that allows us to move the robots – in a future post we'll analyse geometry and use that to specify the movements.

Ollie and BB-8

The "controller" code we saw in the last post needed a little updating for use in this way. I went ahead and stripped out the keyboard-related code – as we're using behind a web-service – and added the capability to control individual robots. We want to be able to move the robots independently, for example. We also want to use the controller in the API layer, so we return the controller object from a module export.

Here's the updated controller:

For the server implementation, we make use of Express to specify various routes:

All these APIs are effectively "GET" end-points: it seemed unnecessary (for now) to add the complexity of requiring PUT requests to "modify" the robots. It certainly makes testing easier, as you can just open the link in a standard browser.

Here's the server code:

Running this in Node allows us to move our robots from within a browser.

To get this all running side AutoCAD, we can now use a simple WebClient to make various DownloadString() calls to the various URLs. Here's the AutoCAD client code:

using Autodesk.AutoCAD.ApplicationServices;

using Autodesk.AutoCAD.EditorInput;

using Autodesk.AutoCAD.Runtime;

using System.Globalization;

using System.Net;

using System.Web.Script.Serialization;

 

namespace DriveRobots

{

  public static class Extensions

  {

    public static string ToTitleCase(this string str)

    {

      return CultureInfo.CurrentCulture.TextInfo.ToTitleCase(str.ToLower());

    }

  }

 

  public class Commands

  {

    private static string _lastBot = "";

    const string host = "http://localhost:8080";

    const string root = host + "/api/robots";

 

    [CommandMethod("DR")]

    public void DriveRobot()

    {

      var doc = Application.DocumentManager.MdiActiveDocument;

      if (doc == null)

        return;

      var ed = doc.Editor;

      var db = doc.Database;

 

      using (var wc = new WebClient())

      {

        string[] names = null;

        try

        {

          var json = wc.DownloadString(root);

          names = new JavaScriptSerializer().Deserialize<string[]>(json);

        }

        catch (System.Exception ex)

        {

          ed.WriteMessage("\nCan't access robot web-service: {0}.", ex.Message);

          return;

        }

 

        // Ask the user for the robot to control

 

        var pko = new PromptKeywordOptions("\nRobot name");

        foreach (var name in names)

        {

          pko.Keywords.Add(name.ToTitleCase());

        }

 

        // If a bot was selected previously, set it as the default

 

        if (!string.IsNullOrEmpty(_lastBot))

        {

          pko.Keywords.Default = _lastBot;

        }

        var pkr = ed.GetKeywords(pko);

 

        if (pkr.Status != PromptStatus.OK)

          return;

 

        _lastBot = pkr.StringResult;

        var botUrl = root + "/" + _lastBot.ToLower();

 

        // Start by getting the bot - this should wake it, if needed

 

        try

        {

          wc.DownloadString(botUrl);

        }

        catch (System.Exception ex)

        {

          ed.WriteMessage("\nCan't connect to {0}: {1}.", _lastBot, ex.Message);

          return;

        }

 

        // The direction can be one of the four main directions or a number

 

        var pio = new PromptIntegerOptions("\nDirection");

        pio.Keywords.Add("Left");

        pio.Keywords.Add("Right");

        pio.Keywords.Add("Forward");

        pio.Keywords.Add("Backward");

        pio.AppendKeywordsToMessage = true;

 

        // Set the direction depending on which was chosen

 

        var pir = ed.GetInteger(pio);

        var direction = "";

        if (pir.Status == PromptStatus.Keyword)

        {

          direction = pir.StringResult;

        }

        else if (pir.Status == PromptStatus.OK)

        {

          direction = pir.Value.ToString();

        }

        else return;

 

        // Generate the URL to direct the robot

 

        var dirUrl = botUrl + "/" + direction.ToLower();

 

        // Our move command

 

        try

        {

          wc.DownloadString(dirUrl);           

        }

        catch (System.Exception ex)

        {

          ed.WriteMessage("\nCan't move {0}: {1}.", _lastBot, ex.Message);

        }

      }

    }

  }

}

 

Let's now see it in action:

 

 

For now you can see that we're really just firing simple directional commands: we're not specifying speed or distance, which we may need to do to follow complex paths with reasonable accuracy. We'll cross that bridge when we come to it, though: at least this outlines a simple approach for the communication to take place.

Leave a Reply

Your email address will not be published. Required fields are marked *