Web Serviceを活用するWeb Applicationの作り方

その他

スポンサーリンク

LabVIEWと同じようなプログラミング感覚でWeb Applicationが作れるソフトとしてLabVIEW NXG Web ModuleあるいはG Web Development Softwareがあります(以下、総じてWeb Application作成ソフト、と言います)。

これらWeb Application作成ソフトを使用することで、最も簡単な、つまりWeb Applicationのみで完結するプログラムを作成する際の手順およびNI Web Serverの構成例は別の記事で紹介しました。

ただし、Web Applicationを作成する大元のプログラム、Web VI上の関数パレットを見てもわかる通り、通常のLabVIEW プログラムでできることがWeb VIではできなかったりします。そもそも関数の数が少ないです。

使用できる関数が限られている以上、Web VIから作成したHTMLでは複雑なことができませんが、それだとやれることが限られてしまい、うまみがないじゃん、ということになります。(もちろん、自分でJavaScriptを書けば機能を追加することはできますが)

一方で、Web VIはHTTPクライアントの関数を使用することができるため、特定のサーバーと通信してリクエストを投げ、その応答を受け取るという動作はできます。

つまり、ある程度複雑な処理はサーバー側に任せ、その結果をクライアント側であるWeb Applicationで受け取るといった役割分担をさせることで、目的の動作を実現できる場合があります。

サーバー側で「ある程度複雑な処理」を行う仕組みがWeb Serviceです。それっぽい言い方をすると、「HTTPリクエストをクライアント(Web Application)から受け取って、HTTPレスポンスを返すプログラム」です。 既にネット上にある様々なサイトも、おおよそサーバーとクライアントでHTTPのリクエスト送信、レスポンス受信という構造は似たような作りになっていると思います。

Web Serviceは、作れるのであればプログラミング言語は問いません。Web Serviceを作れるプログラミング言語の一つにLabVIEWも含まれています。

そこで本記事では、LabVIEWで作成した簡単なWeb Serviceと、Web Application作成ソフトで作成したWeb Applicationとの間で通信を行う際の簡単な流れをまとめました。

なお、注意点として、LabVIEW 2020以降を使用する前提としています。(Web ServiceはLabVIEW 2020よりも前のバージョンで使用できますが、本記事で使用しているNI Web ServerをWeb Service側で選べるのがLabVIEW 2020以降のバージョンなためです)

スポンサーリンク

Web Serviceとは

まずはWeb Serviceになじみがない、という方のために簡単な例を一つお見せします。

Web Serviceの仕組みそのものはLabVIEW VIとして構成します。例えば以下の図のような形です。

これはHTTPリクエストの一つであるGETメソッドを使用し、リクエストを送ることで、現在の時刻を返してくれるプログラムになっています。

実際にウェブブラウザでこれを実行すると現在時刻を表示します。

このようなGETメソッド以外にも、他のHTTPメソッドを用意することができます。

ではこういったWeb Serviceをどうやって用意するかについて、基本的なWeb Serviceの作り方を紹介していきます。

まずはLabVIEWでプロジェクトエクスプローラを用意してこれを開き、マイコンピュータで右クリック、新規からWebサービスを選択します。Webサービス名は後々Web Applicationから接続するWeb Serviceを指定する際に使用するので、デフォルト名ではなく適切な名前にしておくことをオススメします。

次にWebリソースの部分で右クリックして新規のVIを作ります。通常の新規VIとは異なるフロントパネルが出てきます。これがベースとなります。

このVIの名前は、後でURLに表記する際に使用されます。本来VI名にGETなどと入れる必要はないのですが、今回はわかりやすさのためにGETDatetime.viという名前で保存します。

なお、新規VIは必ずHTTPメソッドGET用として生成されます。プロジェクトエクスプローラ上でGETDatetime.vi(GET)などと表示される、この括弧の中身がメソッドを表わします。もし他のHTTPメソッド用のVIを作る場合には、viを右クリックしてPOSTなど他のメソッドを選択します。

さて、今回作るGETDatetime.viの中で、GETのHTTPリクエストに対して応答を返す仕組みを入れます。

やり方は二種類あるのですが、まずはデフォルトの状態で応答を返せる方を紹介します。(他のやり方は後で紹介します)

といってもこれは単純で、応答として返したいデータに表示器をつけ、フロントパネル上でコネクタペーンにその表示器端子を割り当てるだけです。

以下の図は、日付/時間文字列をフォーマット関数の出力であるdatetimeというラベルの付いた文字列表示器を、デフォルトのコネクタペーンの一番右上に対応させている場合の例です。

プログラムができたら、Web Serviceを右クリックして「開始」します。NI Web Serverが構成されている場合、画面に表示される項目をそのまま次へ次へと進めばそれで終わりです。

もしNI Web Serverを構成していない、構成の仕方が分からないという場合には、別記事で紹介していますので参考にしてみてください。なお、冒頭にも書きましたが、Web Service側でNi Web Serverを使用できるのはLabVIEW 2020以降です。(これより前だとNI Application Web Serverという別サーバーで動かすことになります)

また、もし開始を押した後に以下の図のようなエラーが出た場合には、(根本的な原因がよくわからないのですがたまに出ることがあります)LabVIEW.exeを管理者権限で実行してやることで開始できるようになることがあるので試してみます。

ただし管理者権限でLabVIEW自体を開いた後にプロジェクトエクスプローラをダブルクリックすると、管理者権限ではないLabVIEWも同時に立ち上がってしまうので、必ずLabVIEWのファイルメニューの「開く」からプロジェクトを開くようにします。

開始がうまくいったら、ウェブブラウザ上で特定のメソッドをWeb Serviceに投げるようにURL部分を構築していきます。

今回のプログラムの場合、Web Service名がSimpleWebServiceであり、GETメソッドのviの名前がGETDatetimeなので、指定するのはNI Web ServerのURIのあとにSimpleWebService/GETDatetimeというURLになります。(以下の図ではURLを入力しているところにhttp://が表示されていませんが、これを先頭につけます)

正しく実行されれば、日付文字列がJSON文字列として表示されます。

この時の命令は、メソッドURLとしてメソッドVIを右クリックして取得することもできます。ただしこのやり方で表示されるURLは127.0.0.1のローカルIPになります。

大まかに、Web Serviceを用意するためにやることは以上です。

Web Serviceに対するWeb Applicationの役割

今、GETの動作を確かめるために、ウェブブラウザのURL入力に指定のメソッドURLを打ち込みましたが、要はこういった処理をプログラム的に行ってその結果を表示させるのがWeb Applicationの役割になります。

この目的で使用するのがHTTPクライアント関数のGETを始めとした関数群です。HTTPクライアントの関数自体はWeb Application作成ソフトだけではなくLabVIEWにもあります。

LabVIEWにも関数があるということは、Web Application作成ソフトを使用しなくてもLabVIEWで事足りるのでは?と思うかもしれませんが、その通りです。LabVIEWのプログラムであっても、Web Serviceにアクセスするプログラムを組もうと思えば組むことができます。

例えば、上で紹介したGETDatetime.vi(GET)の入ったWeb Serviceを「開始」した状態で、Web Serviceとは関係のない場所に作ったviでHTTP GET.viを使用してブラウザに入力したのと同じURLを入力すると、やはり応答を得ることができます。

なお、後でも出てきますが、GETなどの関数で得られた本文の出力は、ブラウザで表示されていたのと同様JSON文字列となっています。このJSON文字列から他のデータタイプに「変換」することができます。その際には、データタイプとデータの名前(ラベル)をクラスタで指定します。

なお、今回の例で「{“datetime”:~~~}」などと表示されていますが、この中の「datetime」という文字は、VIの中で使用していた文字列表示器のラベルに一致します。

このサンプルでは(JSON)文字列から文字列に変換しているので「うまみ」が分かりにくいですが、後で他のデータタイプに変換する様子を示すので、利点が分かってくると思います。

このように結局LabVIEWでもWeb Serviceへリクエストを送ることはできます。

ただ、LabVIEWではあくまでVIもしくはEXEの形式でプログラム(アプリケーション)上で結果を取得するのに対し、Web Application作成ソフトでは最終的にHTMLファイルを作成するので、ウェブブラウザ上で簡単に表示させることができるという点が異なります。

前者は実行環境(EXEを実行するPC)にLabVIEWランタイムエンジン(およびその他必要なドライバソフトウェア)のインストールが必須です。一方で後者はウェブブラウザに表示させるだけなのでランタイムエンジンなどは必要なく、とにかくウェブブラウザが使用できる環境であればなんだって(PCじゃなくてもタブレットやスマホでも)使用できます。

機能を使いたいPCそれぞれに、LabVIEWランタイムエンジンやその他必要なソフトウェアをインストールするのがメンドクサイ、ムズカシイ、そんな負担はWeb Applicationにはありません。ただウェブブラウザが使用できればいいだけです。(ネットワーク関係のトラブルなど別の問題が起こる可能性があることは置いといて・・・)

具体的なプログラム例

話がそれましたが、本記事の本題として、もう少し複雑なWeb Serviceを作り、これとWeb Application作成ソフトで作成したWeb Applicationとで通信をするプログラムを紹介します。

このWeb Serviceでは、

  • GETリクエストにより、適当な乱数を5つ生成し、レスポンスとして返す
  • POSTリクエストにより、受け取った配列の最大、最小、平均を計算しレスポンスとして返す
  • POSTリクエストにより、受け取った配列、平均、そしてコメントを含んだファイルを作成し、サーバーPC上のデスクトップ上のフォルダ「SampleFolder」にテキストファイルとして保存する。このフォルダの中のファイルの名前をレスポンスとして返す

といった動作をするようにします。

内容としてはそこまで複雑な処理ではないですが、Web ServiceとWeb Applicationの連携、使い方としてこんなことができる、という例にしてみました。

上記はWeb Service側で行う処理の内容ですが、これに応じてWeb ApplicationではそれぞれのHTTPリクエストを送る仕組みを用意する必要があります。

今回の例ではWeb Application側のパネル(ウェブブラウザ上で表示される画面)上にそれぞれのリクエストに対応するブールのボタンやその他Web Serviceに渡すパラメタを用意してみました。

実際に以下で紹介するプログラムがどのような結果となるかをまずはお見せします。

Web Serviceが始まっている状態で、Web Applicationをウェブブラウザから開きます。

開いたら、urlの部分にWeb ServerURLをIPアドレスおよびポート番号まで入力してconnectボタンを押します。これでWeb Serviceに接続状態となります。

Getのボタンを押すと、適当な乱数が表示されます。

次にCalculateのボタンを押すと、今取得した配列の中の最大値、最小値、および5つの数字の平均値が表示されます。

そして、ファイル名や書き残したいコメントを入力した状態でsaveボタンを押すと、処理が行われFileListに保存されたファイル名が表示されます。

サーバー環境のPC上のデスクトップにはSampleFolderというフォルダができており、この中に保存したファイルが入っています。

例えばこういった動作を行うプログラムの中身はどうなっているのか、それらのプログラムの仕組みを次に紹介していきます。

プログラムの構造

Web Service、Web Applicationそれぞれのブロックダイアグラムを紹介します。

Web Serviceプログラム

まずはWeb Service側です。上記3つのHTTPレスポンスを返せるようにそれぞれにしています。

そのため、それぞれに対してHTTPメソッドVIを作成しています。

今回の記事では、冒頭で紹介したような感じで、それぞれのHTTPメソッドVIの名前の最初にGETやPOSTなどといったメソッド名をつけていますが、本来はこのような必要はありません。後でWeb Application側でHTTPメソッドの関数をを使用する際の見やすさでこのようにしているだけです。

まずはGETRandomData.viです。GETリクエストを受けて、適当な整数乱数値を5つ返します。

冒頭の簡単な例(GETDatetime.vi)で示したGETのプログラムとは別の方法でレスポンスを返すプログラムとしています。それは、Webサービスパレットにある、「応答を書き込む」(Write Response)関数と「出力を排出」(Flush Output)関数を使用する方法です。

この方法を使用する場合には、以下の図のように関数を配置し、書き込む応答( 「応答を書き込む」 関数への入力文字列)としてJSON文字列を作るためにJSONに平坦化関数を用いています。

JSONに平坦化関数への入力はクラスタになっています。Dataという名前のクラスタの中に、RandomDataという名前の配列が入っており、このクラスタに対してバンドル関数で乱数配列を入力しています。ここで指定しているRandomDataというラベル名が、後でWeb Application側でJSON文字列を変換する際に使用されます。

次に、受け取ったデータの最大値、最小値、平均値を求めるPOSTDataAnalysis.viです。

このPOSTメソッドも、まずはコネクタペーンを使用してレスポンスを返すプログラム例として紹介します。レスポンスとして返すMaxValue、MinValue、そしてMeanValueそれぞれに表示器を作り、これらをコネクタペーンに割り当てています。

なお、POSTの場合にはWeb Applicationからデータを受け取ることができるので、Webサービスパレットの「ポストデータを受け取る」(Read Postdata)関数を使用します。

受け取ったデータはJSON文字列になっているので、JSONから非平坦化関数を使用しています。JSONから非平坦化関数には、どのような形式のJSON文字列かを指定するためのクラスタを入力しています。

最後に、データをファイル保存するPOSTFileSave.viです。こちらのPOSTメソッドは、GETRandomData同様、コネクタペーンは使用せずにJSON文字列を明示的に作って応答を返すようにするために 「応答を書き込む」関数と「出力を排出」関数 を使用しました。

このプログラムでは、このWeb Serviceが実行されているPCのデスクトップ上にSampleFolderというフォルダを、なければ新たに作成したうえで、txtファイルとして保存するようにしています。

さて、3つのHTTPメソッドVIを紹介してきましたが、GETRandomData.viとPOSTFileSave.viは 「応答を書き込む」関数と「出力を排出」関数 を使ってレスポンスを返す作りにしました。

この方法をとる場合の注意点として、Web Service側でプロパティの設定を正しく行う必要があります。このプロパティはWeb Serviceを右クリックして開くことができます。

プロパティにはHTTPメソッドの設定に対して出力タイプが選べ、デフォルトでは端子となっています。これは、POSTDataAnalysisのように、端子(表示器)をコネクタペーンに指定した場合用の設定です。

出力タイプには、端子の他にストリームが選べます。これは GETRandomData.viとPOSTFileSave.viのように「応答を書き込む」関数と「出力を排出」関数を使ってJSON文字列をレスポンスとして返す作りのプログラムに対して選びます。

そのためこれらの違いは、それぞれのメソッドVIにて、端子、要は表示器を用意してこれらをコネクタペーンに設定しているか、あるいは「応答を書き込む」関数と「出力を排出」関数を使用してデータを送っているかの違いになります。

それぞれのメソッドVIでどのように指定していたかということとWeb Serviceのプロパティから見えるそれぞれのHTTPメソッドの出力タイプを正しく設定しないと、Web Serviceからの応答をうまく受け取れないので注意します。

Web Applicationプログラム

今度はWeb Application側のプログラムを用意します。こちらも、それぞれのHTTPリクエストに応じてサブgviを用意するという書き方にしています。こうすることで、各リクエストのプログラムの管理がしやすくなります。

今回のサンプルでは、Web Serviceとの対応を分かりやすくするため、各サブgviもあえてWeb Serviceと同じ名前にしています。まずはGETRandomDataのプログラムです。

url制御器は、メインのプログラムから受け取る、「http://<IP>:<ポート番号>/」の部分のことです。Web Service側からは Dataという名前のクラスタの中に、RandomDataという名前の配列 が入っていてそれがJSON文字列になっていたことを踏まえて、受け取る側であるこのプログラムでも全く同じクラスタを用意しJSONから非平坦化の関数とクラスタプロパティを用いてデータを取得しています。

なお、クラスタ名であるDataは必ずしもWeb Service側と一致させる必要はありませんが、プログラムの管理上、クラスタ内部の要素のラベルだけでなくクラスタ自身のラベルも一致させる方が読みやすいと思います。

次はPOSTDataAnalysisのプログラムです。POSTの場合にはWeb Servie側にデータを送れますが、こちらについてもWeb Service側のJSONから非平坦化で指定していたクラスタと全く同じ構造、名前のクラスタをJSONに平坦化してPOST関数に入力しています。

Web Serviceが、POSTDataAnalysisリクエストに対して返すレスポンスについて、Web Service側のPOSTDataAnalysisは「JSONに平坦化」を使用していなかったもののやはりJSON文字列となっています(デフォルトの場合)。そのため、このWeb Application側のPOSTの関数の本文出力で得られた文字列はJSON文字列となっており、「JSONから非平坦化」する流れは先ほどのGETRandomDataの場合と全く変わりません。

最後にPOSTFileSaveプログラムです。こちらも上2つと同じく、JSON文字列でのWeb Serviecとのやり取りであることを意識して、Web Service側で使っていたクラスタと中身が同じものを使用します。

3つともでとにかく大事なのは、Web Service側でどのようにデータを送っていたかという状態と全く同じ名前でJSONに平坦化あるいはJSONから非平坦化してやることです。

私は、明示的にクラスタを指定するやり方の方が一貫性があるため、Web Service側もコネクタペーンを使用せずに 「応答を書き込む」関数と「出力を排出」関数 を使用しWeb Serviceプロパティの出力タイプをストリームにするやり方が好きですが、これは好みによると思います。

それぞれのプログラムが完成したら、これらをメインのプログラム(gviweb)に組み込めば完成です。

メインとなるgviweb、index.gviwebのパネルは以下のようにしています。

gviweb上で使用できる関数は普通のviと比べて制限はありますが、ループやイベントストラクチャなどは一通りそろっているので、ここではイベントストラクチャをベースにしたステートマシンを構成してみました。

そのイベントに入る前には、urlの指定を行えるようにループを設けています。この部分がないと、Webブラウザを開いた瞬間にプログラムが実行され、urlを入力する間がなくなります。urlはプログラム完成後にもサーバー環境やWeb Service側の都合で変わる可能性があるため、Web Application側で可変の入力としておきたいので、定数にするのは不便になります。

イベントは3つ用意しています。

そしてこれらのイベントに対してケースを設けています。

これらのプログラムを作成してから、実際に実行する手順としては

  • Web Serviceを「開始」する
  • Web Applicationを実行する

という流れになります(順番は逆でもいいですが、web serviceが開始されていないとWeb Applicationで正しい応答が得られないため、基本的には上記の順での操作が適切です)。

Web Applicationの実行については、もしWeb Applicationソフトウェア上で行う場合にはそのまま実行ボタンを押すだけです。

もしこれらをビルドして作成されたhtmlファイルがあって、これを適切な場所(NI Web Serverでホストしている場合にはC:\Program Files\National Instruments\Shared\Web Server\htdocsのパス)に入れている場合には、Google ChromeなどのWebブラウザを開いて、目的のURLにアクセスした時点で開始されます。

この辺りの手順はWeb Application単体で動かすのとまったく同じです。別記事で紹介しているので、よかったら参考にしてみてください。

うまくいけばWeb ServiceとWeb ApplicationとでHTTPリクエスト、レスポンスのやりとりで、期待通りの結果を得られます。

注意点

次に、Web Service、Web Applicationで通信を行うプログラムに関していくつかの注意点を紹介していきます。

デバッグについて

今までに紹介してきたプログラムの中身をそっくりそのまま書けばプログラムは動くはずです。が、それでも全然期待した結果が返ってこないという場合にはデバッグをする必要があります。

Web Service側のプログラムを確かめる場合には、適当に何か値を入れて結果を確かめるのが望ましいです。こちらはデバッグツール(実行のハイライトやプローブなど)が使えるので比較的トラブルシュートは楽です。

問題は、Web Application側のプログラムのデバッグです。Web Application作成ソフトではデバッグツールが使えないため、頼りになるのはエラー表示器と文字列表示器です。

エラー表示器は、エラーが出ていると思しき部分につけて、何かエラーが起きているかどうかを確認するために使用します。

例えば以下の図は、Web Serviceが開始されていないのにWeb Applicationを実行した場合のエラーの様子です。

こういった、おっちょこちょいなミスによって期待した結果が返ってきていないのはまだ可愛いものですが、Web Serviceを実行しているのに期待した結果が返ってこない場合にはさらに細かくエラー表示器をつけてどこが元凶か調べることになります。

期待した結果が表示されない原因は、通信上の問題ではなく、JSON文字列の扱い方かもしれません。そこで文字列表示器は、特にJSONから非平坦化の関数で使用し、Web Serviceからの応答が期待通りの値になっているかを確かめるのに使用します。

例えば、JSONから非平坦化関数で指定するクラスタの構造もWeb Service側と合わせているのに、上の図のような位置に文字列表示器を付けた結果GETやPOSTの関数からそもそも期待している値が返ってきていない場合には、Web Service側のプロパティの出力タイプを疑うことが必要かもしれません。(デフォルトでは「端子」になっています)

接続が成功しているかを確認する

Web ServiceとWeb Applicationの通信が上手くいかない、というのはプログラム完成後にも起こりうることです。ネットワークの状態(IPアドレスなど)が変わることでURLの指定が今までの値と異なっているがそれに気づいていない、といったことが起こりえると思います。

しかし、プログラム完成後(URLさえ正しければ通信ができるはずの状態になった後)は通信ができるはずなのにできないことについて気づくのが難しかったりします。

そこで、私個人としては、まずそもそもWeb Serviceとの通信が上手くいっているかを確かめるというステップを一番最初に設けるようにしています。

例えば、HTTPリクエストを送るサブgviをどれでもいいので(後の処理に影響しないものを選びます)選択し、この中のHTTPリクエストの関数がエラーなく実行されるかどうかで接続がうまくいったかどうかを判断させます。

この記事で扱ったプログラムで言えば、例えば以下のようにします。GETRandomDataのプログラムからのエラー出力にNotを付けて、これを外側のWhileループの条件端子に接続しています。

つまり外側のWhileループが終わって、イベントストラクチャが入ったWhileループにいくためには、GETRandomDataのプログラムからエラーが出ない状態となっている必要があり、このプログラムからエラーが出ていないのでURLの入力が正しいと判断できます。

あるいは、接続がうまくいったかどうかを確かめるためだけに専用のHTTPメソッドをWeb Service側に用意し、これに対応するHTTPリクエストのプログラムをWeb Application側に盛り込むという手もありますが、メンドクサイ場合には既存のプログラムを使っても問題ありません。

CORSの設定

Web Applicationをデバッグしていく中で、CORSが何たら、というエラーに遭遇するしたら、これを防ぐための工夫をWeb Service側に持たせます。

CORS自体は、複数のドメインをまたぐようなWeb Service側の作りにする場合に設定するもので、日本語では「オリジン間リソース共有」とされます。

特にこういったことを意識しないで書いたプログラムでもCORSがなんちゃらかんちゃらというエラーメッセージが出ることがあり、これを防ぐためにWeb Service側のプログラムに以下のサブVIを付けておきます(これはHTTPメソッドVIとは別、つまり普通のLabVIEW VIとして用意しておきます)。

作ったサブVIは、各HTTPメソッドVIの一番最初につけるようにしておきます。

なお、CORSの設定はWeb ServiceやWeb ApplicationをホストしているNI Webサーバの構成でも行えます。

本記事ではWeb ApplicationからWeb Serviceと通信することで色々な処理が行えるようになるための基本的な流れを紹介してきました。

実際、本記事で紹介した「5つの乱数を取得する」みたいなことはWeb Application内でもできることではありますが、Web Application側では対応していないような処理も本記事と似たようなプロセスでWeb Serviceを駆使して行えるようになりますので参考にして頂ければ嬉しいです。

ここまで読んでいただきありがとうございました。

コメント

タイトルとURLをコピーしました