使用Amazon Simple Queues Service (SQS)实现与AutoCAD远程交互

亚马逊Amazon作为云计算的领跑者推出了很多云服务,我的同事Gopinath Taget在AutoCAD DevBlog上写了一篇文章介绍了SQS与AutoCAD的结合应用。SQS即可以理解为一个放在云上的消息队列,先进先出(FIFO)。【更正,云端的队列与我们常规的队列稍有不同,不能保证顺序是严格的先进先出(FIFO),你从后面的演示例子就可以看出,出队列的顺序可能和进队列时不一样】保存在队列中的消息有一定时间的存活期。通过SQS,我们可以实现位于不同地方的不同程序在不同的时间内进行通信。 比如我可以从位于北京的一个普通桌面程序发送消息到亚马逊简单队列服务(SQS),发送完成后即可退出。其后位于北美的AutoCAD应用程序可以通过读取存储到SQS上的消息来完成北京发出的指令。

 

要使用Amazon SQS服务,你需要首先注册一亚马逊云服务账号,现在亚马逊提供了为期一年的免费服务。你可以参考峻祁连的另一片文章《免费的云技术平台--亚马逊WEB服务AWS提供免费方式》。亚马逊提供了几种SDK,包括Java, Python, .net. 这里使用.net来掩饰。AWS .NET SDK可以从这里下载。安装好AWS .NET SDK后可以通过添加到AWSSDK.dll的引用来调用AWS提供的服务。

 

下面我们就来模拟这个过程。首先建立一个普通的windows form应用程序作为发送端。添加到AWSSDK.dll的引用,这个dll位于C:\Program Files (x86)\AWS SDK for .NET\bin目录下。

image

 

首先需要创建一个对象来连接到Amazon SQS。连接到SQS需要你的Access Key和Secret Key。这两个key可以从这里找到。可以通过下面的方式把AccessKey和Secret Key以写在代码中。

AmazonSQS sqs = AWSClientFactory.CreateAmazonSQSClient(
    <Access Key placeholder>,  
    <Secret Key placeholder>);

更好的方式是写在配置文件App.Config中,如下:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <appSettings>
    <add key="AWSAccessKey" value="*****"/>
    <add key="AWSSecretKey" value="*****"/>
  </appSettings>
</configuration>

这样在创建AmazonSQS对象时直接用如下语句即可。程序会从App.Config中查找相关的Key信息。

//Create AzazomSQS client using information of appconfig.xml
AmazonSQS sqsClient = AWSClientFactory.CreateAmazonSQSClient();

 

这个发送端程序要完成的工作就是向远程的AutoCAD发送工作序列,其中有三个任务:

"Say: Hello AutoCAD" : 在AutoCAD命令行中输出这个信息;

"Action: drawline" : 在AutoCAD中画一条线;

"Action: drawcircle" : 在AutoCAD中画一圆。

 

下面是发送端的完整代码:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using Amazon;
using Amazon.SQS;
using Amazon.SQS.Model;

namespace SQS_Sender
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void button1_Click(object sender, EventArgs e)
        {
            //Create AzazomSQS client using information of appconfig.xml
            AmazonSQS sqsClient = AWSClientFactory.CreateAmazonSQSClient();
            //AmazonSQS sqs =
            //    AWSClientFactory.CreateAmazonSQSClient(
            //    <Access Key placeholder>, 
            //    <Secret Key placeholder>
            //     );

            try
            {
                //Create a queue if it does not exits
                CreateQueueRequest sqsRequest = new CreateQueueRequest();
                sqsRequest.QueueName = "AutoCADJobQueue";
                CreateQueueResponse sqsResponse = sqsClient.CreateQueue(sqsRequest);
                
                string myQueueUrl = string.Empty; ;
                if (sqsResponse.IsSetCreateQueueResult())
                {
                    myQueueUrl = sqsResponse.CreateQueueResult.QueueUrl;
                }

                //post a message to the queue
                SendMessage(sqsClient, myQueueUrl, "Say: Hello AutoCAD");
                SendMessage(sqsClient, myQueueUrl, "Action: drawline");
                SendMessage(sqsClient, myQueueUrl,"Action: drawcircle");
                
            }
            catch (AmazonSQSException aEx)
            {
                MessageBox.Show(aEx.Message);
               
            }

        }

        private static void SendMessage(AmazonSQS sqsClient, 
                                        string myQueueUrl, 
                                        string messageBody)
        {
            SendMessageRequest sendMsgRequest = new SendMessageRequest();
            sendMsgRequest.QueueUrl = myQueueUrl;
            sendMsgRequest.MessageBody = messageBody;
            sqsClient.SendMessage(sendMsgRequest);
        }
    }
}

 

下面完成接收端。接收端一个AutoCAD插件,我们可以利用AutoCAD 2010-2012 dotNet Wizards.zip来创建一个插件模版,然后添加到AWSSDK.dll的引用。

image

 

直接贴出完整代码如下:

 

using System;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.EditorInput;
using Autodesk.AutoCAD.Geometry;
using Autodesk.AutoCAD.Runtime;
using System.Collections.Generic;

using Amazon;
using Amazon.SQS;
using Amazon.SQS.Model;

// This line is not mandatory, but improves loading performances
[assembly: CommandClass(typeof(SQS_AutoCADReceiver.MyCommands))]

namespace SQS_AutoCADReceiver
{

    // This class is instantiated by AutoCAD for each document when
    // a command is called by the user the first time in the context
    // of a given document. In other words, non static data in this class
    // is implicitly per-document!
    public class MyCommands
    {
        [CommandMethod("DoJobFromCloud",CommandFlags.Modal)]
        public void DoJobFromCloud() //this method can be any name
        {
            Document doc = Application.DocumentManager.MdiActiveDocument;
            Database db = doc.Database;
            Editor ed = doc.Editor;

            //get the queue client
            AmazonSQS sqsClient = AWSClientFactory.CreateAmazonSQSClient(
                "<your access key>",
                "<your secret key>"
                );

            try
            {
                string myQueueUrl = string.Empty;

                //checking whether the queue exits or not
                ListQueuesRequest lstQueueRequest = new ListQueuesRequest();
                ListQueuesResponse lstQueueResponse = sqsClient.ListQueues(lstQueueRequest);
                if (lstQueueResponse.IsSetListQueuesResult())
                {
                    ListQueuesResult listQueueResult = lstQueueResponse.ListQueuesResult;

                    foreach (string queueUrl in listQueueResult.QueueUrl)
                    {
                        ed.WriteMessage(queueUrl+ "\n");
                        if (queueUrl.Contains("AutoCADJobQueue"))
                        {
                            myQueueUrl = queueUrl;
                        }   
                    }
                }

                if (myQueueUrl.Length > 0) //The queue exits
                {
                    //Get the message number in the queue
                    int numMessage = GetMessageNumber(sqsClient, myQueueUrl);

                    if (numMessage > 0)
                    {
                        do
                        {
                            //receive a message
                            ReceiveMessageRequest receiveReq = new ReceiveMessageRequest();
                            receiveReq.QueueUrl = myQueueUrl;
                            ReceiveMessageResponse receiveRes = sqsClient.ReceiveMessage(receiveReq);

                            if (receiveRes.IsSetReceiveMessageResult())
                            {
                                foreach (Message msg in receiveRes.ReceiveMessageResult.Message)
                                {
                                    if (msg.IsSetBody())
                                    {
                                        if (msg.Body.StartsWith("Say"))
                                        {
                                            //print the message in AutoCAD
                                            ed.WriteMessage(msg.Body + "\n");
                                        }
                                        if (msg.Body.StartsWith("Action"))
                                        {
                                            if (msg.Body == "Action: drawline")
                                            {
                                                //draw a line in AutoCAD
                                                DrawLine();
                                                ed.WriteMessage(msg.Body + " executed!\n");
                                            }
                                            if (msg.Body == "Action: drawcircle")
                                            {
                                                //draw a circle in AutoCAD
                                                DrawCircle();
                                                ed.WriteMessage(msg.Body + " executed!\n");
                                            }
                                        }
                                    }

                                    //delete the message from queue, 
                                    //since we have aleady worked on it
                                    DeleteMessageRequest delMsgRequest = new DeleteMessageRequest();
                                    delMsgRequest.QueueUrl = myQueueUrl;
                                    delMsgRequest.ReceiptHandle = msg.ReceiptHandle;
                                    sqsClient.DeleteMessage(delMsgRequest);
                                }
                            }

                            numMessage = GetMessageNumber(sqsClient, myQueueUrl);
                        } while (numMessage > 0);
                    }


                }


            }
            catch (AmazonSQSException aEx)
            {
                ed.WriteMessage("\n Error: " + aEx.Message);
            }

        }

        private static int GetMessageNumber(AmazonSQS sqsClient, string myQueueUrl)
        {
            int numMessage = 0;
            GetQueueAttributesRequest req = new GetQueueAttributesRequest();
            req.AttributeName.Add("All");
            req.QueueUrl = myQueueUrl;
            GetQueueAttributesResponse res = sqsClient.GetQueueAttributes(req);

            if (res.IsSetGetQueueAttributesResult())
            {
                GetQueueAttributesResult attResult = res.GetQueueAttributesResult;
                numMessage = attResult.ApproximateNumberOfMessages;
            }
            return numMessage;
        }

        public void DrawCircle()
        {
            Document doc = Application.DocumentManager.MdiActiveDocument;
            Database db = doc.Database;

            using (Transaction trans = db.TransactionManager.StartTransaction())
            {
                BlockTable bt = trans.GetObject(db.BlockTableId, OpenMode.ForWrite) as BlockTable;
                BlockTableRecord modelSpace = trans.GetObject(bt[BlockTableRecord.ModelSpace], OpenMode.ForWrite) 
                                           as BlockTableRecord;

                using (Circle c = new Circle())
                {
                    c.Center = new Point3d(0, 0, 0);
                    c.Radius = 10;
                    modelSpace.AppendEntity(c);
                    trans.AddNewlyCreatedDBObject(c, true);
                }

                trans.Commit();
                
            }
        }

        public void DrawLine()
        {
            Document doc = Application.DocumentManager.MdiActiveDocument;
            Database db = doc.Database;

            using (Transaction trans = db.TransactionManager.StartTransaction())
            {
                BlockTable bt = trans.GetObject(db.BlockTableId, OpenMode.ForWrite) as BlockTable;
                BlockTableRecord modelSpace = trans.GetObject(bt[BlockTableRecord.ModelSpace], OpenMode.ForWrite) 
                                           as BlockTableRecord;

                using (Line ln = new Line())
                {
                    ln.StartPoint = new Point3d(0,0,0);
                    ln.EndPoint = new Point3d(10, 10, 0);
                    modelSpace.AppendEntity(ln);
                    trans.AddNewlyCreatedDBObject(ln, true);
                }

                trans.Commit();

            }
        }

    }

}

下面是在AutoCAD中的运行结果:

image

 

你觉得还有哪些情形可以使用Amazon简单队列服务(SQS)?欢迎评论大家一起讨论。

 

源码下载:http://dl.dropbox.com/u/48146765/SQS_AutoCAD.zip 


Related Posts Plugin for WordPress, Blogger...