I more or less completed the research for my cloud-based round-trip 2D Revit model editing project.
Here are the main items in my discussion of that and one or two other cloud and mobile related topics:
I more or less completed the research for my cloud-based round-trip 2D Revit model editing project.
Here are the main items in my discussion of that and one or two other cloud and mobile related topics:
Posted at 02:00 AM in Browser, Client, Cloud, HTML, HTML5, Javascript, Jeremy Tammik, Mobile, Scaling, Script, Server, Storage | Permalink | Comments (0)
Technorati Tags: Jeremy Tammik, Revit API
|
Continuing the research and development for my cloud-based round-trip 2D Revit model editing project, I updated the project description to emphasise more strongly that I am using pure client-side scripting to display and edit my graphical data, so there is nothing to implement or install at all on the mobile device, beyond testing that my server-side scripts really do their job.
The presentation outline looks like this:
I have completed the implementation of all these steps with the single minute exception of the prefix 'auto'.
In other words, I currently have two commands in my Revit add-in:
Here is a little two-minute film demonstrating the current functionality to give you the idea:
Getting ready for the last stages, now...
Posted at 02:00 AM in Browser, Client, Cloud, HTML, Javascript, Jeremy Tammik, Mobile, Script, Server, Storage | Permalink | Comments (0)
Technorati Tags: Jeremy Tammik, Revit API
|
I’ve been in the past few months playing around with a recent technology from SAP, the market and technology leader in business management software, that they called SAP NetWeaver Gateway.
The main idea is to provide a framework and a set of tools that will allow programmers to develop applications on various platforms, including of course mobile and web, able to connect to an SAP backend system without the need to be an SAP expert.
Here is how SAP themselves define that technology:
SAP NetWeaver Gateway technology provides a simple way to interact with SAP applications through variety of devices, environments and platforms based on market standards. The framework enables development of innovative, people-centric solutions that bring the power of SAP business software into new experiences, such as: social networking and collaboration environments; mobile and tablet devices; and rich internet applications. The framework supports rapid innovation while ensuring security, integrity, management and optimized maintenance of the core SAP systems. Completely flexible, the software offers connectivity to SAP applications using any programming language or model without the need for SAP knowledge by taking advantage of REST services and OData/ATOM protocols.
Below is a very simplified diagram of the concept:
My goal here is not to teach you SAP technologies in general, there are more specialized places for that, but to it’s to illustrate how easy it can be for a programmer with no or little knowledge about SAP to integrate data from an SAP system inside a desktop application such as Autodesk Inventor or AutoCAD. The hard part of the process is to expose your backend system to the NetWeaver Gateway and that requires some SAP skills and knowledge, but once this is achieved, consuming your services from a variety of platforms will be a piece of cake!
In order to let developers investigate the technology, SAP provides a number of sandboxed services that are Gateway ready. I was picking up one among many, the MATERIAL service and created two add-in projects that allow accessing this SAP data from inside Inventor and AutoCAD.
Here is what you need to do in order to start playing with that technology
1 - Sign up for the Gateway services at the SAP NetWeaver Gateway Developer Center
2 - Download and install the SAP NetWeaver Gateway developer tools. As you will see by following that previous link, there are plug-ins for Eclipse, X-Code and Visual Studio. Because I’m developing .Net add-ins for Autodesk desktop products, I only gave a try at the Visual Studio plug-in.
3 - Create a new add-in project, in that example I’m using a C# plug-in for AutoCAD. Select the project item in the solution explorer and right-click on it to let the context menu appear. The SAP plug-in provides a new option “Add SAP Service Reference…”
4 - After clicking on it, a custom dialog will appear allowing you to pick up one or several services available based on the host name you provided. In that case I only select MATERIAL
5 - The NetWeaver plug-in will then generate a few files that are web services wrappers you can start using in your code. You don’t need to worry about the content of those files
6 - The code is pretty straightforward and you will find several examples provided by SAP from the links I point out below. In my case, I was writing two methods GetMaterials that will return a list of n first materials from the database and FilterMaterials that returns materials according to a selection criteria. Both methods are using LINQ in order to perform queries.
//////////////////////////////////////////////////////
// SAP Connector Utility
//
//////////////////////////////////////////////////////
class SapConnector
{
MATERIAL.MATERIAL _service;
KeyValueConfigurationCollection _settings;
//////////////////////////////////////////////////
// Constructor
//
//////////////////////////////////////////////////
public SapConnector()
{
FileInfo fi = new FileInfo(
System.Reflection.Assembly.
GetExecutingAssembly().Location);
string configPath =
fi.DirectoryName + "\\" + "addin.config";
Configuration config =
ConfigurationManager.OpenMappedExeConfiguration(
new ExeConfigurationFileMap {
ExeConfigFilename = configPath },
ConfigurationUserLevel.None);
_settings = config.AppSettings.Settings;
}
//////////////////////////////////////////////////
// Initialize SAP web service
//
//////////////////////////////////////////////////
public void InitializeSAP()
{
string url = "http://gw.esworkplace.sap.com"
+ "/sap/opu/sdata/IWCNT/MATERIAL/";
_service = new MATERIAL.MATERIAL(
new Uri(url));
_service.Credentials = new NetworkCredential(
_settings["SAPLogin"].Value,
_settings["SAPPassword"].Value);
}
//////////////////////////////////////////////////
// Returns list of count materials
//
//////////////////////////////////////////////////
public List<MATERIAL.Material> GetMaterials(
int count)
{
try
{
List<MATERIAL.Material> result =
new List<MATERIAL.Material>();
if (_service == null)
return result;
var query =
(from MATERIAL.Material material in
_service.MaterialCollection
select material).Take(count);
foreach (MATERIAL.Material material in
query)
{
result.Add(material);
}
return result;
}
catch (Exception ex)
{
return null;
}
}
//////////////////////////////////////////////////
// Returns list of filtered materials
//
//////////////////////////////////////////////////
public List<MATERIAL.Material> FilterMaterials(
int count,
string expression,
bool contain)
{
try
{
List<MATERIAL.Material> result =
new List<MATERIAL.Material>();
var query =
(from MATERIAL.Material material in
_service.MaterialCollection
where (contain ?
material.MaterialDescription ==
expression :
material.MaterialDescription !=
expression)
select material).Take(count);
foreach (MATERIAL.Material material in
query)
{
result.Add(material);
}
return result;
}
catch (Exception ex)
{
return null;
}
}
}
Finally, I was adding a bit of UI around those methods, in order to integrate my plug-ins inside Inventor and AutoCAD. Below is a screenshot of the Inventor version:
Here are some links concerning SAP and the NetWeaver Gateway technology:
You can download my Inventor and AutoCAD projects with full source code below:
Posted at 03:22 PM in Cloud, Philippe Leefsma, Server, Web Server | Permalink | Comments (0)
|
I am continuing the research and development for my cloud-based round-trip 2D Revit model editing project.
To recapitulate, the grand plan is to grab a 2D floor plan with furniture and equipment layout from the BIM, upload it to the cloud, and make it accessible for viewing on mobile devices:
Furthermore, I am enabling some simple editing operations on the mobile device, e.g. to move the furniture and equipment around in the room a bit, update the cloud-based data repository, and reflect these changes back into the desktop BIM:
All of this with absolutely nil installation on the mobile device, and thus totally ubiquitous, using SVG and server-side scripting.
Here are some issues I tackled since my last report:
Check it out, and please let us know what you think!
Posted at 02:00 AM in Browser, Client, Cloud, HTML, Javascript, Jeremy Tammik, Mobile, Other, Server, Storage | Permalink | Comments (0)
Technorati Tags: Jeremy Tammik, Revit API
|
I mentioned the idea of using CouchDB and IrisCouch as a simple cloud-based data repository option, and Philippe picked up that idea to demonstrate a simple and efficient restful database using those components to host and consume images of CAD model data.
Now I presented my own example of pushing desktop data to the cloud via a DreamSeat CouchDB client to store 2D plan view room and furniture boundaries in a cloud-hosted data repository.
Check it out, and please let us know what you think of it!
Posted at 02:00 AM in Cloud, Jeremy Tammik, Storage | Permalink | Comments (0)
Technorati Tags: Jeremy Tammik, Revit API
|
There are several useful web services out there that expose APIs like the Facebook APIs, Twitter APIs, Google Plus APIs, Etrade APIs and so on.
Most of these web platforms do not expose their web service APIs freely. They require you to register as a developer and are issued an API Key (Eg: twitter, google).
I have been trying to understand how the API keys work and if they hold any value as a security mechanism. The short answer is, using API keys as a security mechanism is a bad idea. This is because there is no easy way to hide an API key. Consider this: You write a .NET based desktop application that accesses twitter accounts and displays tweets associated with an account. For this your .NET application makes use of the twitter API. But to use this API, your application needs to use API keys.
However, this means the API key needs to be distributed with your application to your clients. You could try to secure it by encrypting it or embedding it in a binary file etc. But a determined developer with reasonable skills will be able to get to your key eventually.
So what then is the purpose of the API key? From what I have learnt so far, the API key is used more as a tool to collect metrics and identify the application that is making API calls. This is so the web service provider can determine how much their service is being used and by whom. This could help them charge the application user based on how much of the service (storage, compute power, network traffic etc) is being used.
Posted at 06:46 PM in Cloud, Gopinath Taget, Security | Permalink | Comments (0)
|
During our annual team meeting last week, my colleague Jeremy Tammik, stumbled across an interesting cloud-oriented database technology called CouchDB. Basically it consists of a database that runs as a service and that can be interacted with using RESTful requests. Here are three things you should like about it: CouchDB is simple, it works great, it is free and open-source!
-
A more complete definition can be found on its dedicated website:
CouchDB is a database that completely embraces the web. Store your data with JSON documents. Access your documents with your web browser, via HTTP. Query, combine, and transform your documents with JavaScript. CouchDB works well with modern web and mobile apps. You can even serve web apps directly out of CouchDB. And you can distribute your data, or your apps, efficiently using CouchDB’s incremental replication. CouchDB supports master-master setups with automatic conflict detection.
After installing it on your Windows or Mac OSX machine, it is literally a matter of minutes before you start playing with it. The 15 minutes QuickStart guide will help you to get on track.
The first step will be to create a new database to start creating and accessing records in it:
1 .Browse to http://localhost:5984/_utils/
in order to open the admin console
2. Click "Create Database"
3. Enter "adndb" (database name supports lower case and digits only)
You can then create a new record by sending a REST request containing JSON formatted entry. Using a tool like Fiddler for example or the Firefox REST Client , I am sending the following request:
-
Header
Content-Type: application/json
-
URL
http://localhost:5984/adndb/postrecordid
-
Body
{ "Author" : "Phil", "Blog" : "Cloud and Mobile", "Post" : "CouchDB" }
-
As you can see in the screenshot of the admin console below, a new record gets created in my local database:
-
-
The next thing I wanted to do was obviously hosting my CouchDB on the cloud, so I could use it in a concrete cloud-based application. This is made very easy by IrisCouch which provide free hosting for CouchDB, at least when the amount of data you are transiting is low. All you need to do is signup, provide your custom CouchDB domain name and you can start accessing your cloud database like we did locally. The CouchDB technology also made db replication very straightforward, so you can all the setup locally, and move your data to the cloud once you are ready!
--
My CouchDB app
-
I was reusing the project layout from my previous post previous post and migrate that to rely on CouchDB, starting with the .Net console and clients. From the various SDKs you can find that help wrapping CouchDB requests in .Net, I chose one called DreamSeat.
-
In that previous post I was using the cool async/await feature of .Net 4.5 to perform my asynchronous non-blocking calls. Although support asynchronous programming, Dreamseat is designed for lower version of the .net framework and didn’t support the async by default. As an exercise, I wrote a couple of wrapper methods, as extension of Dreamseat classes, so I could use async/await in my own code.
-
Here is an example of what my extension method looks like:
-
public static class CouchClientExtensions
{
public static async Task<CouchDatabase> GetDatabaseAsync(
this CouchClient client,
string databaseName,
bool createIfNotExists)
{
return await Task<CouchDatabase>.Factory.StartNew(() =>
{
try
{
return client.GetDatabase(
databaseName,
createIfNotExists);
}
catch
{
return null;
}
});
}
}
-
This allows to write very compact and elegant asynchronous code, for example the snippet below will access the CouchDB database and refresh the list of all the models I have uploaded previously:
-
private async Task RefreshCloudModels()
{
_tvModels.Nodes.Clear();
_modelIdNodeMap.Clear();
TreeNode root = _tvModels.Nodes.Add(
"_cloudModelsKey", "Cloud Models", 0, 0);
try
{
if (_db == null)
_db = await _couchClient.GetDatabaseAsync(
CouchDbName, true);
var docs = await _db.GetAllDocumentsAsync();
foreach (var row in docs.Rows)
{
var doc = await _db.GetDocumentAsync<JDocument>(row.Id);
ModelInfo mi = new ModelInfo(doc);
if (!_modelIdNodeMap.ContainsKey(mi.ModelId))
{
TreeNode node = root.Nodes.Add("", mi.ModelId, 1, 1);
node.Tag = mi;
_modelIdNodeMap[mi.ModelId] = node;
}
}
root.Expand();
Cursor = Cursors.Default;
}
catch (Exception ex)
{
Cursor = Cursors.Default;
System.Windows.Forms.MessageBox.Show(ex.Message);
}
}
-
In the attached project, you will find a complete implementation of the .Net client and console allowing for upload and viewing of pictures.
-
Posted at 04:00 PM in Client, Cloud, Philippe Leefsma | Permalink | Comments (0)
|
I searched for a simple cloud-based data repository option and found one that seems simpler and more transparent than the Microsoft Azure and Amazon solutions discussed here so far: NoSQL, CouchDB, and IrisCouch.
Check it out, and please let us know what you think of this alternative!
Posted at 03:00 AM in Amazon Web Services, Azure, Cloud, Jeremy Tammik, Mobile, Scaling, Server, Storage, Web Server | Permalink | Comments (0)
|
Attending the Evans Data Developer Relations conference on Monday, I was lucky enough to find myself sitting next to John Musser at lunch. John is the founder of ProgrammableWeb. Embarrassingly (and probably because I’m relatively new to the cloud API scene), I’d never come across this website before. The site attempts to provide an up-to-date directory of mashups, web APIs, and related news.
If you’ve not visited the site before, then its worth taking a look. The API lists will likely give you some ideas for mashups and integrations you could play with.
BTW John gave an excellent and very informative presentation at the conference on API business models.
Posted at 12:58 PM | Permalink | Comments (0)
|
By Daniel Du
This is the last part of this topic. In this post, I will introduce how I created the AutoCAD plugin to monitor the AutoCAD command usage and upload it to cloud.
firstly, I created an AutoCAD plugin project with the help of AutoCAD.net plugin wizard. AutoCAD related references are added automatically by the wizard.
To use my custom data model, I need to add referenced to my data model project, which is created in part 1. It is simple to monitor the AutoCAD command with Document Events in AutoCAD API, for example CommandEnded wraps the AcEditorReactor.commandEnded() ObjectARX function, it indicates that a command has complete. So firstly register the Document.CommandEnded event:
private Dictionary<string, int> _commandHitDic = new Dictionary<string, int>();
[CommandMethod("ACV_MonitorCommandEvents")]
public void MonitorCommandEvents_Method()
{
SubscribeToDoc(Application.DocumentManager.MdiActiveDocument);
GetEditor().WriteMessage("Magic ball is listening, keep working... ");
}
public void SubscribeToDoc(Document doc)
{
doc.CommandEnded += new CommandEventHandler(doc_CommandEnded);
}
void doc_CommandEnded(object sender, CommandEventArgs e)
{
string commandName = e.GlobalCommandName;
// filter out the custom commands of this application
if (commandName.StartsWith("ACV"))
{
return;
}
AddCommandHit(commandName);
}
private void AddCommandHit(string commandName)
{
if (_commandHitDic.Keys.Contains<string>(commandName))
{
_commandHitDic[commandName]++;
}
else
{
_commandHitDic.Add(commandName, 1);
}
}
By this code snippet, I am listening to the CommandEnded event and put the hit number of command into a a dictionary.
Next step is to upload the command statistics up to the RESTful service on Windows Azure from the dictionary. To send REST request, I am using a popular library RestSharp here, you may want to read Stephen’s post about usage of RestSharp here.
[CommandMethod("ACV_UpdateToCloud")]
public void UpdateToCloud()
{
UserCommandsHit usrCmdsHit = null;
RestClient client = new RestClient(BASE_URL);
client.AddHandler("application/json", new JsonDeserializer());
usrCmdsHit = GetUserCommadsHitByUserName(_userName, client);
//CommandHit record with this username is not found in cloud
//Add one record for this user.
if (usrCmdsHit == null)
{
//add user command hit record
usrCmdsHit = BuildNewUserCommandsHitFromDictionary(_userName,_commandHitDic);
//POST to add new
AddNewToCloud(usrCmdsHit, client);
}
else
{
//update the user command hit with dictionary
usrCmdsHit = UpdateUserCommandsHitFromDictionary(usrCmdsHit,_commandHitDic);
//PUT to update
UpdateToCloud(usrCmdsHit, client);
}
GetEditor().WriteMessage("\n Your command usage statastic has been updated to cloud succesfully.");
GetEditor().WriteMessage("\n Keep working or open http://acadcommandwebviewer.cloudapp.net/ with a modern broswerto view the magic ball ;) ");
GetEditor().WriteMessage("\n Chrome/Firefox are recommended. ");
System.Diagnostics.Process.Start("http://acadcommandwebviewer.cloudapp.net/");
}
private UserCommandsHit GetUserCommadsHitByUserName(
string userName,
RestClient client)
{
UserCommandsHit usrCmdsHit = null;
RestRequest reqGet = new RestRequest();
reqGet.Resource = "api/AcadCommands";
reqGet.Method = Method.GET;
reqGet.RequestFormat = DataFormat.Json;
reqGet.AddHeader("Accept", "Application/json");
reqGet.JsonSerializer = new RestSharp.Serializers.JsonSerializer();
reqGet.AddParameter("username", userName);
var respGet = client.Execute<UserCommandsHit>(reqGet);
if (respGet.StatusCode == System.Net.HttpStatusCode.OK)
{
if (respGet.Data != null)
{
usrCmdsHit = respGet.Data;
}
else
{
usrCmdsHit = Newtonsoft.Json.JsonConvert
.DeserializeObject<UserCommandsHit>(respGet.Content);
}
}
return usrCmdsHit;
}
private UserCommandsHit BuildNewUserCommandsHitFromDictionary(
string userName,
Dictionary<string, int> commandHitDic)
{
UserCommandsHit usrCmdsHit = new UserCommandsHit();
usrCmdsHit.UserName = userName;
List<CommandHit> list = new List<CommandHit>();
foreach (var cmdName in commandHitDic.Keys)
{
CommandHit ch = new CommandHit
{
CommandName = cmdName,
HitNumber = commandHitDic[cmdName]
};
list.Add(ch);
}
usrCmdsHit.CommandHits = list;
return usrCmdsHit;
}
private UserCommandsHit UpdateUserCommandsHitFromDictionary(
UserCommandsHit usrCmdsHit,
Dictionary<string,int> commandHitDic)
{
foreach (var cmdName in commandHitDic.Keys)
{
int count = usrCmdsHit.CommandHits
.Where<CommandHit>(p => p.CommandName == cmdName)
.Count<CommandHit>();
if (count == 0)
{
CommandHit ch = new CommandHit
{
CommandName = cmdName,
HitNumber = commandHitDic[cmdName]
};
usrCmdsHit.CommandHits.Add(ch);
}
else
{
CommandHit ch = usrCmdsHit.CommandHits
.First<CommandHit>(p => p.CommandName == cmdName);
ch.HitNumber += commandHitDic[cmdName];
}
}
return usrCmdsHit;
}
private void AddNewToCloud(UserCommandsHit usrCmdsHit, RestClient client)
{
RestRequest reqPost = new RestRequest();
reqPost.Resource = "api/AcadCommands";
reqPost.Method = Method.POST;
reqPost.RequestFormat = DataFormat.Json;
reqPost.AddHeader("Content-Type", "application/json");
reqPost.JsonSerializer = new RestSharp.Serializers.JsonSerializer();
reqPost.AddBody(usrCmdsHit);
var respPost = client.Execute<UserCommandsHit>(reqPost);
if (respPost.StatusCode == System.Net.HttpStatusCode.Created)
{
_commandHitDic.Clear();
GetEditor().WriteMessage("\nUpdate to cloud successfully.");
}
else
{
GetEditor().WriteMessage("\n Error:" + respPost.StatusCode);
GetEditor().WriteMessage("\n" + respPost.Content);
}
}
private void UpdateToCloud(UserCommandsHit usrCmdsHit, RestClient client)
{
RestRequest reqPut = new RestRequest();
reqPut.Resource = "api/AcadCommands/" + usrCmdsHit.Id ;
reqPut.Method = Method.PUT;
reqPut.RequestFormat = DataFormat.Json;
reqPut.AddHeader("Content-Type", "application/json");
reqPut.JsonSerializer = new JsonSerializer();
reqPut.AddBody(usrCmdsHit);
var respPut = client.Execute<UserCommandsHit>(reqPut);
if (respPut.StatusCode == System.Net.HttpStatusCode.OK)
{
_commandHitDic.Clear();
GetEditor().WriteMessage("\nUpdate to cloud successfully.");
}
else
{
GetEditor().WriteMessage("\n Error:" + respPut.StatusCode);
GetEditor().WriteMessage("\n" + respPut.Content);
}
}
Okay, with that I am wrapping up this post, hopefully you got the idea what I did, and hope it is helpful for you.
Posted at 11:28 PM in Cloud, Daniel Du, WebGL | Permalink | Comments (0)
|
Subscribe