Design Automation API を利用するアプリでは、多くの場合、Web ブラウザをフロントエンド インタフェースに使用して、エンドユーザが Design Automation API の AppBundle(アドイン)が処理すべきパラメータを指定します。
パラメータは、 JSON データとて WorkItem の実行領域にファイル(.json)に保存されるので、アドインが JSON ファイルを読み取り、成果ファイルの生成や編集などに反映することが出来ます。
Web ブラウザからパラメータを JSON 渡しする際の Activity や WorkItem の定義は Forge Online - Design Automation:AutoCAD タスクの自動化 の記事(AppBundle での Design Automation)で、アドインが JSON ファイルを読み取る具体的な方法は、AutoCAD アドインの Design Automation API 化 の記事で、それぞれご紹介しています。
さて、場合によっては、逆に 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
コメント
コメントフィードを購読すればディスカッションを追いかけることができます。