2
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

グラフィカルな簡易監視画面の作成

Last updated at Posted at 2024-09-30

はじめに

WebアプリケーションサーバやDBの状態を監視するにはGrafanaの様なダッシュボードが使用されるケースが多いと思います。これで多くの場合問題ありませんが、工場設備やエレベータ、発送電、上下水等の監視にはもう少しグラフィカルな図付きの監視画面が使用されています。一般用途向けでも、ソーラーパネルの発電量を表示するシステムは、単に数値だけではなく図付きのポップな画面を良く見ます。今回はこの様なグラフィカルな監視画面をWebで作成してみました。

実現にはOSSであるWinterCardinal GraphicEditorを利用しています。また、作成したサンプルはGitHubにプッシュしてあります。

本記事では、作成したサンプルをベースに、WinterCardinal GraphicEditorの使い方について紹介します。

WinterCardinal GraphicEditorとは?

今回使用しているWinterCardinal GraphicEditorはブラウザ上で動作するグラフィックエディター兼ビューワーです。内部的にはWebGL、PixiJSWinterCardinal UIが使用されており、InkScapeMicrosoft Visioの様にグラフィックを作成するためのものです。ただしInkScapeやVisioが静的な絵を作成するものであるのに対し、GraphicEditorは電力量等の値に応じて動的に変化する絵を作成する事を目的としています。例えば電力量がある一定値を超えたら、文字を赤く大きくして警告すると言った表現が出来ます。この点ではPowerPointのアニメーションに似ています。PowerPointのアニメーションはクリックやページ遷移契機で動作するのに対し、GraphicEditorではセンサー値を契機として動作します。

InkScapeやVisioでも動きに関する情報を埋め込み、SVGとしてエクスポート、専用ビューワを開発する事で似たようなことは可能です。過去にはその様な事を行ってきましたが、どうしても無理や煩雑さが生じていました。またSVGゆえにグラフィックが大きくなるとロードに時間を要する問題もありました。GraphicEditorはそれ専用に設計されており、またSVGと比較し非常に軽量なファイルフォーマットとなっています。

またGraphicEditorはWeb上で動作するため、同じくWeb上で動作する監視システムであれば、GraphicEditorをシステムに組み込む事が可能です。これによって設備に変化が生じた時にもグラフィックをその場で変更して動かすと言う事が容易に出来るようになります。

作成したサンプルの概要

GitHubに置いたサンプルをREADMEに従ってビルド・起動すると、下記のような画面が表示されます。

sign-in.png

ログイン後、左サイドバーのDiagramを選択すると下のような画面が表示されます。センサー値に応じて絵がアニメーションしますので、これを見ながら監視を行うという想定のサンプルになっています。

diagram-with-side-menu.gif

また、左サイドバーのGraphic Editorを選択し、plantと言う名前のファイルを読み込むと上のDiagram画面の元となったグラフィックが表示されます。

graphic-editor-1.png

このGraphic Editorで表示したいグラフィックを作成し、Diagram画面に切り替えて表示することによって、画面の作成・変更・確認をWebブラウザ上でグラフィカルかつシームレスに行えます。

上記サンプルではWinterCardinal GraphicEditorをホストするためのAPサーバとしてSpring BootWinterCardinalH2 DBを使用しています。アイコンはMaterial Iconsから拝借しています。

画面がブラウザ上に表示されるまでの流れは下記の通りです。

画面を最初に表示する時はブラウザ側からplantのグラフィックデータ要求を出し、H2 DBに入っているグラフィックデータを返して表示します。グラフィックの表示はGraphicEditorが行ってくれます。

overall-step1.png

その後センサー値を収集し、GraphicEditorがグラフィックを更新する(=数値の更新とタンク容量アニメーション)と言う流れになります。センサー値のプッシュにWinterCardinal(=WebSocket)を使用していますが、更新頻度が低ければHTTPでポーリングしても良いかと思います。

overall-step2.png

グラフィックの作成・表示はGraphic Editorを使ってブラウザ上から行えますが、Graphic Editor自体のホスティングや、送られてくるセンサ値との連携などにはコーディング作業が必要となります。詳細はGitHubのサンプルコードをご覧いただければと思いますが、概要を次に説明します。

Graphic Editor画面を実装する

まずグラフィックを作成・編集するためのGraphic Editor画面を実装します。MVCコントローラを新設し/graphic-editorにアクセスされたらsrc\main\resources\templates\graphic-editor\main.htmlを表示するように設定します。

src\main\java\app\mvc\MvcGraphicEditorController.java
@Controller
public class MvcGraphicEditorController {
	@GetMapping("/graphic-editor")
	public String main() {
		return "graphic-editor/main";
	}
}

合わせてサイドメニューからGraphic Editorを選択すると/graphic-editorに遷移する様に設定します。

src\main\resources\templates\top\menu.html
-<button class="wcs-menu-item alert">
+<button class="wcs-menu-item alert" url="./graphic-editor">
	<th:block th:replace="~{image/editor}"></th:block>
	<span th:text="#{top.menu.graphic-editor}">Graphic Editor</span>
</button>

src\main\resources\templates\graphic-editor\main.html側ではWinterCardinalやPixiJSと言った必要なJavaScriptを読み込みます。

src\main\resources\templates\graphic-editor\main.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org" th:with="title=#{graphic-editor.label}">
<head>
	<th:block th:insert="~{common/header}"></th:block>
	<th:block th:insert="~{common/banner::head (${title})}"></th:block>
</head>
<body>
	<th:block th:insert="~{common/banner::body (${title})}"></th:block>
	<th:block th:insert="~{common/csrf}"></th:block>
+	<script th:src="@{/webjars/wcardinal/wcardinal.worker.min.js}"></script>
	<script th:src="@{/graphic-controller}"></script>
+	<script th:src="@{/webjars/pixi.js/pixi.min.js}"></script>
+	<script th:src="@{/webjars/wcardinal-ui/wcardinal-ui.min.js}"></script>
+	<script th:src="@{|/webjars/wcardinal-ui/wcardinal-ui-theme-white-${#strings.toLowerCase(#locale.language)}-${#strings.toLowerCase(#locale.country)}.min.js|}"></script>
+	<script th:src="@{/webjars/wcardinal-geditor/wcardinal-geditor.min.js}"></script>
+	<script th:src="@{|/webjars/wcardinal-geditor/wcardinal-geditor-theme-default-${#strings.toLowerCase(#locale.language)}-${#strings.toLowerCase(#locale.country)}.min.js|}"></script>
+	<script th:src="@{|/asset/message-script/${product.version}/message-script-${#strings.toLowerCase(#locale.language)}-${#strings.toLowerCase(#locale.country)}.min.js|}"></script>
+	<script th:src="@{/webjars/wcardinal-starter/wcs-graphic-editor.min.js}"></script>
	<script>
	(function(){
		"use strict";
		new wcs.Main({
			controller: graphicController,
			csrf: window.csrf
		});
	}());
	</script>
</body>
</html>

最後に読み込んでいるwebjars/wcardinal-starter/wcs-graphic-editor.min.jssrc\main\typescript\graphic-editor\main.tsをRollupでバンドルしたものです。src\main\typescript\graphic-editor\main.ts内のMainクラスがwindow.wcs.Mainにエクスポートされる様に設定してあります。ですのであとはこのMainクラスを実装すれば良いと言う事になります。

src\main\typescript\graphic-editor\main.tsの実装

グラフィックエディターを画面に出すにはWinterCardinal GraphicEditorが提供するFGraphicEditorと言うクラスを使用します。例えば以下の様にしますとエディタがbody直下に生成されます。

src\main\typescript\graphic-editor\main.ts
export class Main extends FGraphicEditor {
	constructor(options: MainOptions) {
		super({
			controller: options.controller,
			header: newHeader("graphic-editor.label")
		});
	}
}

任意の位置にエディターを生成するには以下の様にrootを指定します。

import { FGraphicEditor } from "@wcardinal/wcardinal-geditor";

export class Main extends FGraphicEditor {
	constructor(options: MainOptions) {
		super({
			application: {
				root: "#my-root" // DOM要素のセレクタまたはDOMそのもの
			},
			controller: options.controller,
			header: newHeader("graphic-editor.label")
		});
	}
}

ここでoptions.controllerはHTML側で渡したgraphicControllerとなります。graphicControllerはWinterCardinal GraphicEditorのFGraphicEditorControllerOptionsインターフェイスを実装したクラスで、グラフィックの検索やDBへの保存を担当するクラスです。GraphicEditorのサンプルに実装例があります。

今回作成した簡易監視画面ではJava側のapp.wcc.graphic.GraphicControllerをHTMLから<script th:src="@{/graphic-controller}"></script>で読み込んで、それをそのまま渡すと言う実装になっています。

src/main/resources/templates/graphic-editor/main.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org" th:with="title=#{graphic-editor.label}">
<head>
	<th:block th:insert="~{common/header}"></th:block>
	<th:block th:insert="~{common/banner::head (${title})}"></th:block>
</head>
<body>
	<th:block th:insert="~{common/banner::body (${title})}"></th:block>
	<th:block th:insert="~{common/csrf}"></th:block>
	<script th:src="@{/webjars/wcardinal/wcardinal.worker.min.js}"></script>
+	<script th:src="@{/graphic-controller}"></script>
	<script th:src="@{/webjars/pixi.js/pixi.min.js}"></script>
	<script th:src="@{/webjars/wcardinal-ui/wcardinal-ui.min.js}"></script>
	<script th:src="@{|/webjars/wcardinal-ui/wcardinal-ui-theme-white-${#strings.toLowerCase(#locale.language)}-${#strings.toLowerCase(#locale.country)}.min.js|}"></script>
	<script th:src="@{/webjars/wcardinal-geditor/wcardinal-geditor.min.js}"></script>
	<script th:src="@{|/webjars/wcardinal-geditor/wcardinal-geditor-theme-default-${#strings.toLowerCase(#locale.language)}-${#strings.toLowerCase(#locale.country)}.min.js|}"></script>
	<script th:src="@{|/asset/message-script/${product.version}/message-script-${#strings.toLowerCase(#locale.language)}-${#strings.toLowerCase(#locale.country)}.min.js|}"></script>
	<script th:src="@{/webjars/wcardinal-starter/wcs-graphic-editor.min.js}"></script>
	<script>
	(function(){
		"use strict";
		new wcs.Main({
+			controller: graphicController,
			csrf: window.csrf
		});
	}());
	</script>
</body>
</html>

Java側のapp.wcc.graphic.GraphicControllerをJavaScript側で読み込む機能はWinterCardinalによるものです。<script th:src="@{/graphic-controller}"></script>でクラスを読み込むと、window.graphicControllerにクラスのインスタンスが生成されます。

GraphicControllerに定義されたメソッドやフィールドの内、@Callable@Componentを付与されたものがJavaScriptから見える様になります。

app.wcc.graphic.GraphicControllerの実装

GraphicControllerはグラフィックの検索やDBへの保存を担いますが、今回の簡易監視画面ではグラフィックデータをDBからSELECT、UPSERT、DELETEする単純なものとなっています。

src\main\java\app\wcc\graphic\GraphicController.java
@Controller
public class GraphicController {
	@Autowired
	protected GraphicComponent graphic;
}
src\main\java\app\wcc\graphic\GraphicComponent.java
@Component
public class GraphicComponent {
	@Autowired
	GraphicService service;

	@Autowired
	GraphicPieceComponent piece;

	/** 検索文字を名称に含むグラフィック一覧を返す */
	@Callable
	Iterable<GraphicFound> search(String word) {
		return service.search(word);
	}

	/** 指定のIDのグラフィックデータを返す */
	@Callable
	Graphic get(final Long id) {
		return service.find(id).orElse(null);
	}

	/** 指定の名称のグラフィックデータ返す */
	@Callable
	Graphic getByName(final String name) {
		return service.find(name).orElse(null);
	}

	/** グラフィックを保存するメソッド */
	@Callable
	Long save(final Graphic graphic) {
		return service.save(graphic).getId();
	}

	/** 指定のIDのグラフィックを削除する */
	@Callable
	void delete(Long id) {
		service.delete(id);
	}
}

GraphicComponent#pieceはグラフィックピースと呼ばれる別のグラフィックを扱うためのものです。グラフィックピースはVisioで言うステンシルの様なもので、グラフィックの中に埋め込むことが出来るグラフィックです。Graphic Editor画面では左側のパズルのアイコンからグラフィックピースを埋め込む事が出来ます。グラフィックピースの自体の作成はGraphic Piece Editor画面から行える様にしています。

graphic-piece-button.png

Diagram画面を実装する

次にグラフィックを表示するDiagram画面を作成します。そのためまずMVCコントローラを新設し、/diagramにアクセスするとsrc\main\resources\templates\diagram\main.htmlを返却する様に設定します。

src\main\java\app\mvc\MvcDiagramController.java
@Controller
public class MvcDiagramController {
	@GetMapping("/diagram")
	public String main() {
		return "diagram/main";
	}
}

合わせてサイドメニューからDiagramを選択すると/diagramに遷移する様に設定します。

src\main\resources\templates\top\menu.html
-<button class="wcs-menu-item diagram">
+<button class="wcs-menu-item diagram" url="./diagram">
	<th:block th:replace="~{image/diagram}"></th:block>
	<span th:text="#{top.menu.diagram}">Diagram</span>
</button>

src\main\resources\templates\diagram\main.html側ではGraphic Editor画面と同様にWinterCardinalやPixiJSと言った必要なJavaScriptを読み込んでいます。

src\main\resources\templates\diagram\main.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org" th:with="title=#{diagram.label}">
<head>
	<th:block th:insert="~{common/header}"></th:block>
	<th:block th:insert="~{common/banner::head (${title})}"></th:block>
</head>
<body>
	<th:block th:insert="~{common/banner::body (${title})}"></th:block>
	<th:block th:insert="~{common/csrf}"></th:block>
+	<script th:src="@{/webjars/wcardinal/wcardinal.worker.min.js}"></script>
	<script th:src="@{/diagram-controller}"></script>
+	<script th:src="@{/webjars/pixi.js/pixi.min.js}"></script>
+	<script th:src="@{/webjars/wcardinal-ui/wcardinal-ui.min.js}"></script>
+	<script th:src="@{|/webjars/wcardinal-ui/wcardinal-ui-theme-white-${#strings.toLowerCase(#locale.language)}-${#strings.toLowerCase(#locale.country)}.min.js|}"></script>
+	<script th:src="@{|/asset/message-script/${product.version}/message-script-${#strings.toLowerCase(#locale.language)}-${#strings.toLowerCase(#locale.country)}.min.js|}"></script>
+	<script th:src="@{/webjars/wcardinal-starter/wcs-diagram.min.js}"></script>
	<script>
	(function(){
		"use strict";
		new wcs.Main({
			controller: diagramController,
			csrf: window.csrf
		});
	}());
	</script>
</body>
</html>

最後に読み込んでいるwebjars/wcardinal-starter/wcs-diagram.min.jssrc\main\typescript\diagram\main.tsをRollupでバンドルしたものです。src\main\typescript\diagram\main.ts内のMainクラスがwindow.wcs.Mainにエクスポートされるように設定してあります。ですのであとはこのMainクラスを実装していけばよいと言う事になります。

src\main\typescript\diagram\main.tsの実装

WinterCardinal GraphicEditorはグラフィックを表示するためのクラスFGraphicViewerも提供しています。FGraphicViewerが生成する画面には、グラフィック選択ボタンがあり、そのボタンを押すとグラフィック選択&表示できると言う画面構成になっています。しかしながら今回はDiagram画面表示後即座にplantグラフィックを表示したかったためFGraphicViewerを使用しないで実装しました。WinterCardinal UIが提供するDDiagramを使用すれば比較的簡単に実装する事が可能です。

export class Main {
	protected _application: DApplication;
	protected _controller: DiagramController;
	protected _diagram?: DDiagram;

	constructor(options: MainOptions) {
		const application = new DApplication();
		this._application = application;

		// センサー値を受信した時の処理
		const controller = options.controller;
		this._controller = controller;
		controller.instant.on("update", (e: unknown, values: Record<string, InstantValue>) => {
			this.onInstantUpdate(values);
		});

		// グラフィックを表示するdiagramをステージに登録
		const diagram = this.diagram;
		application.stage.addChild(diagram);
	}

	// センサー値を受信した時に呼び出されるメソッド
	protected onInstantUpdate(values: Record<string, InstantValue>): void {
		const data = this.diagram.data;
		for (const sensorName in values) {
			const value = values[sensorName];
			data.set(sensorName, value.value, value.time);
		}
		this._application.update();
	}

	// グラフィックを描画するDDiagram部品
	protected get diagram(): DDiagram {
		return (this._diagram ??= this.newDiagram());
	}

	protected newDiagram(): DDiagram {
		const result = new DDiagram({ ... });
		this._controller.graphic.getByName("plant").then((graphic) => {
			result.set(DDiagrams.toSerialized(graphic));
		});
		return result;
	}
}

DDiagramクラスの#set(...)メソッドにグラフィックデータを渡すことでグラフィックが表示されるようになります。newDiagram()メソッドの中でresult.set(...)している所がそれになります。

今回は使用しませんでしたが、仕様が合えばFGraphicViewerなら上記の様な実装も省略できます。FGraphicViewerを使用した実装例はGraphicEditorのサンプルから見る事ができます。

DDiagramも様々なカスタマイズが可能です。WinterCardinal UIのサンプルからDiagramを参照してください。

センサー値はサーバからプッシュされて送られてきますが、プッシュされた時にoptions.controller.instantにおいてupdateイベントが発生する様にしています。このイベントの第2引数がセンサー値のマップですのでこれをthis.diagram.data.set(...)に渡してグラフィックを更新しています。

options.controllerはHTML側で渡したdiagramControllerですが、これはGraphic Editor画面の場合と同様に、Java側のapp.wcc.diagram.DiagramController<script th:src="@{/webjars/wcardinal-starter/wcs-diagram.min.js}"></script>で読み込んだものです。

src\main\resources\templates\diagram\main.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org" th:with="title=#{diagram.label}">
<head>
	<th:block th:insert="~{common/header}"></th:block>
	<th:block th:insert="~{common/banner::head (${title})}"></th:block>
</head>
<body>
	<th:block th:insert="~{common/banner::body (${title})}"></th:block>
	<th:block th:insert="~{common/csrf}"></th:block>
	<script th:src="@{/webjars/wcardinal/wcardinal.worker.min.js}"></script>
+	<script th:src="@{/diagram-controller}"></script>
	<script th:src="@{/webjars/pixi.js/pixi.min.js}"></script>
	<script th:src="@{/webjars/wcardinal-ui/wcardinal-ui.min.js}"></script>
	<script th:src="@{|/webjars/wcardinal-ui/wcardinal-ui-theme-white-${#strings.toLowerCase(#locale.language)}-${#strings.toLowerCase(#locale.country)}.min.js|}"></script>
	<script th:src="@{|/asset/message-script/${product.version}/message-script-${#strings.toLowerCase(#locale.language)}-${#strings.toLowerCase(#locale.country)}.min.js|}"></script>
	<script th:src="@{/webjars/wcardinal-starter/wcs-diagram.min.js}"></script>
	<script>
	(function(){
		"use strict";
		new wcs.Main({
+			controller: diagramController,
			csrf: window.csrf
		});
	}());
	</script>
</body>
</html>

app.wcc.diagram.DiagramControllerの実装

DiagramControllerの実装は以下の様になっています。GraphicComponentはGraphic Editor画面を作成する時に作ったクラスです。新設したのはセンサー値を収集するInstantComponentです。

@Controller
public class DiagramController {
	@Autowired
	protected InstantComponent instant;

	@Autowired
	protected GraphicComponent graphic;
}

InstantComponentは以下の様に定義しています。sensorsに格納されたセンサーIDのリストに基づきtriggerDirect("update", values)でJavaScript側にupdateイベントを1秒周期で発火させると言うコードになっています。実際の監視システムでは物理デバイスから値を収集する事になりますが、今回はダミーの値を流しています。

@Component
@RequiredArgsConstructor
public class InstantComponent extends AbstractController {
	// このフィールドは型がSClassのためJavaScript側から変更できます
	// 値が変更されると@OnChange("sensors")が付与されているonSensorsChangeが呼び出されます
	@Autowired
	protected SClass<List<String>> sensors;

	// sensorsが変更された時に呼び出されるメソッド
	@OnChange("sensors")
	protected void onSensorsChange(final List<String> sensors) {
		final var values = new HashMap<String, InstantValue>();
		for (int i = 0; i < sensors.size(); ++i) {
			final var sensor = sensors.get(i);
			values.put(sensor, new InstantValue(0, 0, i));
		}
		this.cancelAll(); // 定期処理を全て停止します
		this.interval("send", 0, 1000, values); // 1秒周期でsendメソッドを実行する
	}

	@OnTime
	protected void send(final Map<String, InstantValue> values) {
		final var now = System.currentTimeMillis();
		for (final var value : values.values()) {
			value.update(now);
		}
		this.triggerDirect("update", values); // JavaScript側にupdateイベントを発生させます
	}
}

triggerDirectAbstractControllerが提供しているメソッドで、WinterCardinalの機能です。使用方法はWinterCardinalのCheatsheetJavadocが参考になるかと思います。

DBの初期化

DBの初期化処理はsrc\main\resources\data.sqlで行っています。Spring BootがこのSQLをサーバ起動時に実行してくれます。ただしSpring Boot 2.5からdata.sqlの実行タイミングが変更されているためspring.jpa.defer-datasource-initialization=trueを指定しています。

src\main\resources\data.sql
insert into account (id, name, password) values
	(nextval('account_seq'), 'Account1', '{bcrypt}$2a$10$vmG149.oKSDVFxishLcodelvvAoL.u1yw1Q5tlGObqfhbXQZoxHMe');

insert into sensor (id, name, color) values
	(nextval('sensor_seq'), 'Sensor1', 0xc00000),
	(nextval('sensor_seq'), 'Sensor2', 0xff0000),
	(nextval('sensor_seq'), 'Sensor3', 0xffc000),
	(nextval('sensor_seq'), 'Sensor4', 0xffff00),
	(nextval('sensor_seq'), 'Sensor5', 0x92d050),
	(nextval('sensor_seq'), 'Sensor6', 0x00b050),
	(nextval('sensor_seq'), 'Sensor7', 0x00b0f0),
	(nextval('sensor_seq'), 'Sensor8', 0x0070c0),
	(nextval('sensor_seq'), 'Sensor9', 0x002060),
	(nextval('sensor_seq'), 'SensorA', 0x7030a0);

insert into graphic (version, id, name, label, category, summary, description, data) values
	(1, 1, 'plant', 'Plant', null, '', '', '{"width":1000,"height":500,"background":{"color":16777215,"alpha":1},"tile":{"mapping":{"enable":false,"from":{"lon":-180,"lat":85.05112877980659},"to":{"lon":180,"lat":-85.05112877980659}}},"resources":["","[1,12900086,1]","[0,5464442,1,2,0,15,0]","auto","[1,1]","[0,0]","[0,16777215,1,0.5]","[10,10]","[0,5197647,1,3,14,0,4,5,0,6,5,0,7,0,0]","[0,0,1]","[0,0,0,9,0,0,0,1,0]","[10]","data.value","[2,0,0,1,0.5,1,12]","[0,5197647,1,2,0,11,0]","[]","[1,16777215,0.5]","[1,5464442,1,2,0,15,0]","[1,16777215,1]","[0,15,15,18]","[19,19]","[[-40,-95,40,-95,-40,95,40,95],[],1,20]","[0,16777215,0.5]","[0,5197647,1,2,0,15,0]","Sensor1","[24,5464442,1,3,14,0,4,5,0,6,5,0,7,0,0]","[1,0,0,9,0,0,0,1,0]","[26]","[9,0,2,0,12900086,1,12]","[2,15,15,18]","[29,19]","[[-50,-35,-50,35,50,35],[],2048,30]","[2,0,0,9,0,0,0,1,0]","[32]","[[-70,15,-70,-15,-40,-15,70,-15],[],0,30]","Sensor2","[35,5464442,1,3,14,0,4,5,0,6,5,0,7,0,0]","[1,5197647,1,2,0,15,0]","[29,29]","[[-25,0,25,0],[],0,38]","[3,0,0,9,0,0,0,1,0]","[40]","[9,0,2,1,12900086,1,12]","[[-65,40,-65,-40,65,-40],[],2048,30]","Sensor3","[44,5464442,1,3,14,0,4,5,0,6,5,0,7,0,0]","[[-39.5,0,39.5,0],[],0,20]","[[0,-20,0,20],[],0,30]","[[0,-45,0,45],[],0,38]","[[0,20,0,-20],[],0,30]","[4,0,0,9,0,0,0,1,0]","[50]","[[50,-30,50,30,-50,30],[],2048,30]","Sensor4","[53,5464442,1,3,14,0,4,5,0,6,5,0,7,0,0]","[[30,15,30,-15,-30,-15],[],2048,30]","[[-35,10,-35,60,15,60,15,-60,35,-60],[],0,30]","Sensor5","[57,5464442,1,3,14,0,4,5,0,6,5,0,7,0,0]","Sensor6","[59,5464442,1,3,14,0,4,5,0,6,5,0,7,0,0]","Sensor7","[61,5464442,1,3,14,0,4,5,0,6,5,0,7,0,0]","%d%%","[63,5464442,1,3,14,0,4,5,0,6,5,0,7,0,0]","Math.round(value * 100)","[0,0,65,9,0,0,0,1,0]","[66]","[6,0,1,12]","[5,0,65,9,0,0,0,1,0]","[69]","[6,0,0,9,0,0,0,1,0]","[71]","[6,0,65,9,0,0,0,1,0]","[73]","[3,0,65,9,0,0,0,1,0]","[75]","[2,0,65,9,0,0,0,1,0]","[77]","[1,0,65,9,0,0,0,1,0]","[79]","[4,0,65,9,0,0,0,1,0]","[81]","Base Layer","[0,16777215,1]"],"data":["Sensor7","Sensor1","Sensor2","Sensor3","Sensor4","Sensor5","Sensor6"],"pieces":[],"layers":[[83,3,0,0,500,500,84,1]],"items":[[1,0,895.3,205,79,188.20601184465414,0,0,1,2,-1,8,11,1,12,-1,0,[13],-1,-1,[],0,0,2,-1,-1,27,0,0,0],[24,0,895.2,298.4,79,79,-3.141592653589793,0,1,14,-1,8,15,0.25,15,-1,0,[],-1,-1,[],0,0,2,-1,-1,36,0,0,0],[2,0,320.67595027202145,205,83,270,0,0,16,17,-1,8,15,1,15,-1,0,[],-1,-1,[],0,0,0,-1,-1,2,0,0,0],[5,0,321.00350966810487,206.26566856686998,80,190,0,0,16,17,-1,8,15,0.25,15,21,0,[],-1,-1,[],0,0,0,-1,-1,3,0,0,0],[1,0,173.2999087667538,38.765510492931185,150,40,0,0,22,23,-1,25,15,0.25,15,-1,0,[],-1,-1,[],0,0,0,-1,-1,4,0,0,0],[5,0,221.12709768190766,105,100,70,0,0,16,17,-1,8,27,0.25,15,31,0,[28],-1,-1,[],0,0,0,-1,-1,5,0,0,0],[5,0,141.00350966810484,195,140,30,0,0,16,17,-1,8,33,0.25,15,34,0,[28],-1,-1,[],0,0,0,-1,-1,6,0,0,0],[1,0,71.00350966810485,230,150,40,0,0,22,23,-1,36,15,0.25,15,-1,0,[],-1,-1,[],0,0,0,-1,-1,7,0,0,0],[5,0,246.00350966810484,180,50,0.001,0,0,16,37,-1,8,33,0.25,15,39,0,[28],-1,-1,[],0,0,0,-1,-1,8,0,0,0],[5,0,206.00350966810484,310,130,80,0,0,16,37,-1,8,41,0.25,15,43,0,[42],-1,-1,[],0,0,0,-1,-1,9,0,0,0],[1,0,141.00350966810484,370,150,40,0,0,22,23,-1,45,15,0.25,15,-1,0,[],-1,-1,[],0,0,0,-1,-1,10,0,0,0],[2,0,505.56237190444716,158.77839459189352,82,270,0,0,16,17,-1,8,15,1,15,-1,0,[],-1,-1,[],0,0,0,-1,-1,11,0,0,0],[5,0,505.10443771695526,65.45895714570631,79,0.001,0,0,16,17,-1,8,15,0.25,15,46,0,[],-1,-1,[],0,0,0,-1,-1,12,0,0,0],[5,0,506.0929247688943,254.5415428542937,79,0.001,0,0,16,17,-1,8,15,0.25,15,46,0,[],-1,-1,[],0,0,0,-1,-1,13,0,0,0],[5,0,321.00350966810487,360,0.001,40,0,0,16,17,-1,8,15,0.25,15,47,0,[],-1,-1,[],0,0,0,-1,-1,14,0,0,0],[5,0,321.00350966810487,435,0.001,90,0,0,16,17,-1,8,15,0.25,15,48,0,[],-1,-1,[],0,0,0,-1,-1,15,0,0,0],[3,0,614.2253687511643,270.86321251756783,80,40,0,0,16,17,-1,8,15,0.25,15,-1,0,[],-1,-1,[],0,0,0,-1,-1,16,0,0,0],[0,0,614.2253687511643,240.86321251756783,80,80,0,0,18,17,-1,8,15,0.25,15,-1,0,[],-1,-1,[],0,0,0,-1,-1,17,0,0,0],[2,0,556.911855533147,381,82,221,1.5707963267948966,0,16,17,-1,8,15,1,15,-1,0,[],-1,-1,[],0,0,0,-1,-1,18,0,0,0],[5,0,505.0483404887911,313.2358646565492,0.001,40,0,0,16,17,-1,8,15,0.25,15,47,0,[],-1,-1,[],0,0,0,-1,-1,19,0,0,0],[5,0,612.8308671645913,320.9136787482432,0.001,40,0,0,16,17,-1,8,15,0.25,15,49,0,[],-1,-1,[],0,0,0,-1,-1,20,0,0,0],[5,0,604.4654957643252,98.76257940993423,100,60,0,0,16,17,-1,8,51,0.25,15,52,0,[28],-1,-1,[],0,0,0,-1,-1,21,0,0,0],[1,0,653.5071037852289,41.25179705856203,150,40,0,0,22,23,-1,54,15,0.25,15,-1,0,[],-1,-1,[],0,0,0,-1,-1,22,0,0,0],[5,0,584.4654957643255,185.98913888463437,60,30,0,0,16,17,-1,8,15,0.25,15,55,0,[],-1,-1,[],0,0,0,-1,-1,23,0,0,0],[5,0,754.669098890451,360,0.001,40,0,0,16,17,-1,8,15,0.25,15,47,0,[],-1,-1,[],0,0,0,-1,-1,25,0,0,0],[5,0,754.669098890451,435,0.001,90,0,0,16,17,-1,8,15,0.25,15,48,0,[],-1,-1,[],0,0,0,-1,-1,26,0,0,0],[2,0,895.2828559916301,205,83,270,0,0,16,17,-1,8,15,1,15,-1,0,[],-1,-1,[],0,0,0,-1,-1,37,0,0,0],[5,0,895.6104153877135,360,0.001,40,0,0,16,17,-1,8,15,0.25,15,47,0,[],-1,-1,[],0,0,0,-1,-1,28,0,0,0],[5,0,895.6104153877135,435,0.001,90,0,0,16,17,-1,8,15,0.25,15,48,0,[],-1,-1,[],0,0,0,-1,-1,29,0,0,0],[5,0,812.7482675081753,321.9531452959578,70,120,0,0,16,17,-1,8,15,0.25,15,56,0,[],-1,-1,[],0,0,0,-1,-1,30,0,0,0],[1,0,556.0035096681048,364.18250230763476,150,40,0,0,22,2,-1,58,15,0.25,15,-1,0,[],-1,-1,[],0,0,0,-1,-1,31,0,0,0],[1,0,753.2592292869073,40,150,40,0,0,22,23,-1,60,15,0.25,15,-1,0,[],-1,-1,[],0,0,0,-1,-1,32,0,0,0],[1,0,895.5149489057096,40,150,40,0,0,22,23,-1,62,15,0.25,15,-1,0,[],-1,-1,[],0,0,0,-1,-1,1,0,0,0],[1,0,895,210,150,40,0,0,22,23,-1,64,67,0.25,15,-1,0,[68],-1,-1,[],0,0,0,-1,-1,34,0,0,0],[1,0,555,396.7671588564074,150,40,0,0,22,23,-1,64,70,0.25,15,-1,0,[68],-1,-1,[],0,0,0,-1,-1,35,0,0,0],[1,0,754.3308738467676,205.2353290191372,79,188.20601184465414,0,0,1,2,-1,8,72,1,12,-1,0,[13],-1,-1,[],0,0,2,-1,-1,38,0,0,0],[24,0,754.2308738467677,298.63532901913703,79,79,-3.141592653589793,0,1,14,-1,8,15,0.25,15,-1,0,[],-1,-1,[],0,0,2,-1,-1,39,0,0,0],[2,0,754.3415394943676,205,83,270,0,0,16,17,-1,8,15,1,15,-1,0,[],-1,-1,[],0,0,0,-1,-1,24,0,0,0],[1,0,755,210,150,40,0,0,22,23,-1,64,74,0.25,15,-1,0,[68],-1,-1,[],0,0,0,-1,-1,33,0,0,0],[1,0,140,390,150,40,0,0,22,23,-1,64,76,0.25,15,-1,0,[68],-1,-1,[],0,0,0,-1,-1,40,0,0,0],[1,0,70,250,150,40,0,0,22,23,-1,64,78,0.25,15,-1,0,[68],-1,-1,[],0,0,0,-1,-1,41,0,0,0],[1,0,170,59.535870583066384,150,40,0,0,22,23,-1,64,80,0.25,15,-1,0,[68],-1,-1,[],0,0,0,-1,-1,42,0,0,0],[1,0,655,60,150,40,0,0,22,23,-1,64,82,0.25,15,-1,0,[68],-1,-1,[],0,0,0,-1,-1,43,0,0,0]],"snap":[1,[1,1,[]],[1,1,10]]}');

画面の表示とカスタマイズ

サイドメニューからDiagramを選択し、Diagram画面が表示されるようになります。数値やタンク容量がInstantComponentの生成するセンサー値に応じてアニメーションするのを確認できます。

diagram.png

試しにタンクを1つ消し、色味をグリーンに変更してみます。まず削除するタンクを選択します。左マウスボタンでクリックしながらドラッグすると範囲選択する事が出来ます。

modified-a.png

modified-b.png

選択されたらDeleteキーで削除します。

modified-c.png

色を変更したい場所を選択し、塗り色(Fill)を変更します。

modified-d.png
modified-e.png
modified-f.png

一部の線にアクションと呼ばれる動きの設定がされていますので、そのアクションの色も合わせて変更します。

modified-g.png

この状態で保存し、Diagram画面を表示させると反映されていることを確認できます。

modified-h.png

まとめ

今回はグラフィカルな簡易監視画面をWinterCardinal GraphicEditorで作成してみました。GraphicEditorがWebGLで出来ていることから、SVGと比較して比較的軽量に動作するかと思います。カスタマイズ例としてタンク削除と色変更を行いましたが、設備を増やしたり、クリックで画面遷移できるようにしたり、センサー割り当てを変更したりと言った事も可能です。またより応用的なカスタマイズとして、アプリケーション独自のシェイプやアクションを導入する事も可能になっています。サンプルページに実装例もありますので参考にして頂ければ幸いです。

2
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?