Design Automation API を利用するアプリでは、多くの場合、Web ブラウザをフロントエンド インタフェースに使用して、エンドユーザが Design Automation API の AppBundle(アドイン)が処理すべきパラメータを指定します。
パラメータは、 JSON データとて WorkItem の実行領域にファイル(.json)に保存されるので、アドインが JSON ファイルを読み取り、成果ファイルの生成や編集などに反映することが出来ます。
場合によっては、逆に WorkItem(アドイン)が処理した JSON データの内容を、クライアントの Web ブラウザに渡したい場合があります。

例えば、過去にご紹介した次のような処理を挙げることが出来ます。
クライアント コンピュータからアップロードした図面ファイル内をパース、不要になったブロック定義を削除しつつ、削除したブロック定義に含まれていた図形数をグラフ化してレポートするような処理です。

この処理では、グラフ表示にオープン ソースの Chart.js(https://www.chartjs.org/)を使用しています。Chart.js では、グラフ化する情報を JSON で定義することになります。
例えば、次の円グラフは、後述の JavaScript コードで描画することが可能です。(‘graph’ は、HTML に定義したグラフ領域を示す <canvas> タグに付けた ID です。)

_chart_json = {
type: $('#charttype').text() /*'pie'*/,
data: {
labels: ['Block A', 'Block B', 'Block C', 'Block D', 'Block E', 'Block F'],
datasets: [
{
label: '# of inner element',
data: [
40, 30, 20, 10, 5, 3
],
}
]
},
options: {
plugins: {
colorschemes: {
scheme: 'brewer.Greys7'
}
},
legend: {
position: 'right'
}
}
};
var canvas = document.getElementById('graph');
_chart = new Chart(canvas, _chart_json);
つまり、Design Automation API の AppBundle、ここでは C# を使った .NET API アドインは、図面をパースして、パージしたブロック定義内の要素数をカウント、動的に JSON データを生成する処理の実装が必要になります。
次の C# コードは、アドインが上記処理をする箇所の抜粋です。JSON 生成には、Newtonsoft 社がオープンソースとして公開している Json.NET パッケージ(ライブラリ)を使用しています。
Database db = Application.DocumentManager.MdiActiveDocument.Database;
Log("\nGot database ...");
InputParams inputParams = JsonConvert.DeserializeObject(File.ReadAllText(".\\params.json"));
bool boolPurge = inputParams.purge;
bool boolPreview = inputParams.preview;
Log("\nAddin retrieves Purge:{0}, Preview:{1}", boolPurge, boolPreview);
try
{
using (Transaction tr = db.TransactionManager.StartTransaction())
{
ObjectIdCollection objIds = new ObjectIdCollection();
BlockTable tbl = tr.GetObject(db.BlockTableId, OpenMode.ForRead, false) as BlockTable;
IEnumerator enu1 = tbl.GetEnumerator();
while (enu1.MoveNext())
{
objIds.Add((ObjectId)enu1.Current);
}
db.Purge(objIds);
Log("\nBlockTableRecords are listed by Purge ...");
List strLabels = new List();
List lValues = new List();
List datasets = new List();
ObjectId objId;
BlockTableRecord rec;
long length;
IEnumerator enu2 = objIds.GetEnumerator();
while (enu2.MoveNext())
{
objId = (ObjectId)enu2.Current;
rec = tr.GetObject(objId, OpenMode.ForWrite, false) as BlockTableRecord;
System.Collections.Generic.IEnumerable idCollection = rec.Cast();
length = idCollection.Count();
if (boolPurge)
{
rec.Erase();
Log("\n {0} BlockTableRecord\t - contains {1} entities -\t was purged\n", rec.Name, length);
}
else
{
Log("\n {0} BlockTableRecord\t - contains {1} entities -\t can be purged\n", rec.Name, length);
}
strLabels.Add(rec.Name);
lValues.Add(length);
}
tr.Commit();
datasets.Add(new datasets());
datasets[0].label = "# of inner element";
datasets[0].data = lValues.ToArray();
data data = new data();
data.labels = strLabels.ToArray();
data.datasets = datasets.ToArray();
OutputParams outputParams = new OutputParams();
outputParams.type = "pie";
outputParams.data = data;
outputParams.options = new JRaw("{ \"plugins\": { \"colorschemes\": { \"scheme\": \"brewer.Paired12\" } }, \"legend\": { \"position\": \"right\" } }");
string output = JsonConvert.SerializeObject(outputParams);
using (var file = new StreamWriter(@".\\chart.json", false, System.Text.Encoding.UTF8))
{
file.Write(output);
}
if (boolPurge)
{
string strName = ".\\" + Application.GetSystemVariable("DWGNAME") as string;
Application.DocumentManager.MdiActiveDocument.Editor.Command("QSAVE");
Log("\n{0} was saved ...", strName);
}
}
}
catch (Autodesk.AutoCAD.Runtime.Exception ex)
{
Log(ex.Message);
}
finally
{
}
ログ出力やヘルパークラスの定義は次の通りです。
private static void Log(string format, params object[] args) {
Application.DocumentManager.MdiActiveDocument.Editor.WriteMessage(format, args);
}
public class InputParams
{
public bool purge { get; set; }
public bool preview { get; set; }
}
public class OutputParams
{
public string type { get; set; }
public data data { get; set; }
public JRaw options { get; set; }
}
public class data
{
public string[] labels { get; set; }
public datasets[] datasets { get; set; }
}
public class datasets
{
public string label { get; set; }
public long[] data { get; set; }
}
このアドイン処理によって、WorkItem 実行時の作業領域に、グラフ化する情報が chart.json ファイルとして保存されることが分かります。
もちろん、chart.json ファイルを正しくクライアントに渡せるよう、Design Automation API 側の Activity で chart.json 用の動作を定義、WorkItem で処理するように指定することも必須です。
次の JavaScript コード抜粋は、Activity 定義時に POST activities endpoint へ渡すの JSON Body 記述です。
// Create Activity
var payload =
{
"id": DA4A_UQ_ID,
"commandLine": ['$(engine.path)\\accoreconsole.exe /i "$(args[DWGInput].path)" /al "$(appbundles[PurgeBlock].path)" /s $(settings[script].path)'],
"parameters": {
"DWGInput": {
"zip": false,
"ondemand": false,
"verb": "get",
"description": "Source drawing",
"required": true
},
"Params": {
"zip": false,
"ondemand": false,
"verb": "get",
"description": "Input parameters to specify behavior",
"required": true,
"localName": "params.json"
},
"DWGOutput": {
"zip": false,
"ondemand": false,
"verb": "put",
"description": "Output DWG drawing",
"required": false,
"localName": "purged.dwg"
},
"ChartOutput": {
"zip": false,
"ondemand": false,
"verb": "put",
"description": "Output Chart JSON",
"required": true,
"localName": "chart.json"
}
},
"settings": {
"script": {
"value": "PurgeBlock\n"
}
},
"engine": "Autodesk.AutoCAD+24_1",
"appbundles": [DA4A_FQ_ID],
"description": "Purge Block"
};
同様に POST workitems endpoint に渡す WorkItem の JSON Body は次のようになります。変数 CHART_JSON には、予め、WorkItem 実行時に生成しておいた chart.json 入出力用の Signed URL が格納されている点にご注意ください。
// Create WorkItem
var payload =
{
"activityId": DA4A_FQ_ID,
"arguments": {
"DWGInput": {
"url": signedURLforInput,
"headers": {
"Authorization": "Bearer " + credentials.access_token,
"Content-type": "application/octet-stream"
},
"verb": "get"
},
"Params": {
"url": "data:application/json," + paramsJSON
},
"DWGOutput": {
"url": signedURLforOutput,
"headers": {
"Authorization": "Bearer " + credentials.access_token,
"Content-Type": "application/octet-stream"
},
"verb": 'put',
"localname": SOURCE_DWG
},
"ChartOutput": {
"url": CHART_JSON,
"headers": {
"Authorization": "Bearer " + credentials.access_token,
"Content-Type": "application/json"
},
"verb": 'put'
},
"onComplete": {
"verb": "post",
"url": "-deployed root URL-/api/oncomplete"
}
}
};
今回の例では、確認の目的でクライアント側に Forge Viewer を配置しています。Model Derivative API で変換した SVF/SVF2 を Viewer 上に表示するには、viewables:read の Scope(スコープ)を持つ Access Token(アクセス トークン)を利用するのが一般的です。一方、Design Automation API の各種処理(endpoint 呼び出し)には、code:all の Scope を持つ Access Tokenが必要です。
クライアント側に code:all の Scope を持つ Access Token が渡ってしまうのは、セキュリティ上、好ましい状態ではありませんので、Forge を利用する Web サーバー(Forge アプリ)で独自にルーティングした endpoint を用意して、 code:all の Scope を持つ Access Token をクライアントから隠蔽している点にご注意ください。Forge Viewer を持つ Forge アプリでは、通常、このような実装がおこなわれています。

この例では、GET workitems/:id endpoint を使ったポーリング処理で WorkItem の完了を検出し次第、クライアントから Web サーバー上にルーティングした endpoint を呼び出し、前述の Signed URL からグラフ用に生成した JSON データを得るようになっています。
Node.js で実装した Chart.js 用の JSON 取得用 endpoint 実装は次のとおりです。
// Get Chart.json on bucket
router.get("/get-chart", function (req, res) {
https.get(CHART_JSON, function (chartres) {
var body = '';
chartres.setEncoding('utf8');
chartres.on('data', function (chunk) {
body += chunk;
console.log(" Chart JSON = " + body);
res.end(body);
});
}).on('error', function (e) {
console.log(e.message);
});
});
クライアント(Web ブラウザ)からの上記 endpoint 呼び出し(AJAX)とグラフ更新は、次の JavaScript コードが担います。
// Get Chart JSON
uri = '/api/get-chart';
$.ajax({
url: uri,
type: 'GET',
contentType: 'application/json'
}).done(function (res) {
_chart.data = JSON.parse(_chart_json).data;
_chart.options = JSON.parse(_chart_json).options;
_chart.update();
if (JSON.stringify(JSON.parse(JSON.stringify(JSON.parse(res).data)).labels) === '[]') {
console.log("!!! No blocks to be purgeable");
alert("No blocks to be purgeable");
}
}).fail(function (jqXHR, textStatus, errorThrown) {
console.log('Failed to get Chart JSON: ', jqXHR, textStatus, errorThrown);
});
※ 本記事は 2022年8月 に「Design Automation API:AppBundle からの JSON 取得」から「Design Automation API:WorkItem からの JSON 反映」に改題しました。
By Toshiaki Isezaki

You must be logged in to post a comment.