3
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Angular Materialを使ってMATLAB Production ServerのWebフロントサンプルをちょっとファンシーにしてみた

Last updated at Posted at 2018-06-07

MATLABで作った解析アルゴリズムをシステム化する際に、コンパイルしてMATLAB Production Serverに展開すればRESTful APIでやり取りできるので、JavaScriptからもコールできる。Webフロントエンドを試しに作ってみたのでその備忘録。

サンプル

ドキュメントのExampleに債券計算を題材にしたMATLABコードとHTML/JavaScriptのサンプルがあるので、手始めにまずはここから始めてみる。

HTML/JavaScript側

bptool.htmlとcalculatePrice.jsをコピペしてほぼ完了だが、bptool.htmlの<script src="calculatePrice.js" type="text/javascript"></script>のコメントアウトを外しておく。

MATLAB側

サンプルのpricecalc.mをそのまま使う。

pricecalc.m
function price = pricecalc(face_value, coupon_payment,...
                           interest_rate, num_payments)
    M = face_value;
    C = coupon_payment;
    N = num_payments;
    i = interest_rate;
    
    price = C * ( (1 - (1 + i)^-N) / i ) + M * (1 + i)^-N;

MATLABの「アプリ」→「Production Serverコンパイラ」をクリックしてコンパイル画面を開く。

  • タイプ:デフォルトの「配布可能なアーカイブ」のまま。
  • エクスポートする関数:pricecalc.mを追加
  • アーカイブ情報:「BondTools」に変更

201866_204610.png
これで「パッケージ化」をクリックするとMATLAB Production Server用のパッケージ(.ctfファイル)が作成される。が、今回はWebフロントエンドを作成するのが目的なので、パッケージ化をせずに「クライアントのテスト」をクリックする。これによってMATLAB上でMATLAB Production Serverのもどきが立ち上がる。

「テスト」タブに移るので以下のとおり設定。

  • ポート:デフォルトの9910のまま
  • CORSを有効にする:チェックを入れる

「起動」ボタンをクリックすると、http://localhost:9910/BondTools でサービスがListenするようになる。

201866_211949.png

動作確認

Webサーバーも立てずに、先程のHTML/JavaScriptをPCのデスクトップに置き、ブラウザでbptool.htmlを開く。

スライダーを左右に動かしてパラメータ(Face Value、Coupon Paymant、Number of payments、Interest Rate)に応じて債券価格が計算される。
image.png

MATLABで実行させているMATLAB Production Serverもどきへの入出力もうまくできている。
201866_214852.png

ただ、NativeのHTML/JavaScriptで画面を作っているので、画面がしょぼい。もっとファンシーにしたい。

後半に続く。

もっとファンシーなWebフロントにしたい

どうせならフレームワークを使ってMaterial Designの画面を作ってみようと思ったので、備忘録後半。
AngularのインストールはAngularのQuick Start Guideや他の記事にお任しして、ngコマンドでプロジェクトを作成するところから。

Angularプロジェクトの作成

プロジェクト名はmpsBondCalcという名前にして、ng newでプロジェクトの作成。

これで一通りのAngularのテンプレートが作成される。

Angular-Materialのインストール

プロジェクトのルートフォルダーにチェンジディレクトリして、Angular-Material一式をインストール。

cd mpsBondCalc
npm install --save @angular/material @angular/cdk
npm install --save @angular/animations
npm install --save hammerjs

AngularのGetting Startedの Step 5: Gesture Supportにあるとおり、スライダーを使うにはHammerJSが必須となるので、main.tsにimport 'hammerjs';を追加する。

モジュールにAngular-Materialで今回使用するインプットフォーム、スライダーなどをインポートする(下記のMat*)。

app.module.ts
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { AppComponent } from './app.component';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { MatCardModule, MatInputModule, MatSliderModule, MatDividerModule } from '@angular/material';    
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { HttpModule } from '@angular/http';

@NgModule({
  declarations: [
    AppComponent
  ],
  imports: [
    BrowserModule,
    FormsModule,
    ReactiveFormsModule,
    MatCardModule,
    MatInputModule,
    MatSliderModule,
    MatDividerModule,
    BrowserAnimationsModule,
    HttpModule
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }

メインとなるapp.component.htmlとapp.component.tsは、サンプルのbptool.htmlとcalculatePrice.jsを移植しつつ、DOM操作などAngular用に適宜書き直す。区切り線もbptool.htmlでは<hr>を使っていたが、ここではAngular-Materialのdivider(mat-divider)を使ってみる。

app.component.html
<mat-card class="result">
    <mat-card-content>
        <h1><a>Bond Pricing Tool</a></h1>
        <h2></h2>
        This example shows an application that calculates a bond price from a simple formula.<p>
            You run this example by entering the following known values into a simple graphical interface:<p>
        <ul>
            <li>Face Value (or value of bond at maturity) - M</li>
            <li>Coupon payment - C</li>
            <li>Number of payments - N</li>
            <li>Interest rate - i</li>
        </ul>
        The application calculates price (P) based on the following equation:<p>
            P = C * ( (1 - (1 + i)^-N) / i ) + M * (1 + i)^-N<p>
        <mat-divider [inset]="true"></mat-divider>
        <h3>M: Face Value </h3>
        <form class="example-form form-inline" novalidate>
            <mat-form-field class="example-full-width">
                <input matInput type="number" name="facevalueValue" [(ngModel)]="facevalueValue"  min="0" max="10000" (change)="calculatePrice()">
            </mat-form-field>
            <mat-slider
                name="couponPaymentSlider"
                class="example-margin"
                [max]=10000
                [min]=0
                [(ngModel)]="facevalueValue"
                (change)="calculatePrice()">
            </mat-slider>
        </form>
        
        <h3>C: Coupon Payment </h3>
        <form class="example-form" novalidate>
            <mat-form-field class="example-full-width">
                <input matInput type="number" name="couponPaymentValue" [(ngModel)]="couponPaymentValue"  min="0" max="1000" (change)="calculatePrice()">
            </mat-form-field>
            <mat-slider
                name="couponPaymentSlider"
                class="example-margin"
                [max]=1000
                [min]=0
                [step]=0.01
                [(ngModel)]="couponPaymentValue"
                (change)="calculatePrice()">
            </mat-slider>            
        </form>
        
        <h3>N: Number of payments  </h3>
        <form class="example-form" novalidate>
            <mat-form-field class="example-full-width">
                <input matInput type="number" name="numPaymentsValue" [(ngModel)]="numPaymentsValue"  min="0" max="1000" (change)="calculatePrice()">
            </mat-form-field>
            <mat-slider
                name="numPaymentsSlider"
                class="example-margin"
                [max]=1000
                [min]=0
                [(ngModel)]="numPaymentsValue"
                (change)="calculatePrice()">
            </mat-slider>            
        </form>
        
        
        <h3>i: Interest rate </h3>      
        <form class="example-form" novalidate>
            <mat-form-field class="example-full-width ">
                <input matInput type="number" name="interestRateInput" [(ngModel)]="interestRateValue"  min="0" max="1" step="0.01" (change)="calculatePrice()">
            </mat-form-field>   
            <mat-slider
                name="interestRateSlider"
                id="interest_rate_slider"
                class="example-margin"
                [max]=1
                [min]=0
                [step]=0.01
                [(ngModel)]="interestRateValue"
                (change)="calculatePrice()">
            </mat-slider>            
        </form>

        <h2>BOND PRICE</h2>
        <p id="price_of_bond_value" style="font-weight: bold" #price_of_bond_value>
        <p id="error" style="color:red" #error>

        <mat-divider [inset]="true"></mat-divider>
        <h3>Request to MPS Server</h3>
        <p id="request" #request>

        <h3>Response from MPS Server</h3>
        <p id="response" #response>
        <mat-divider [inset]="true"></mat-divider>
    </mat-card-content>
</mat-card>
app.component.ts
import {Component, ViewChild, ElementRef} from '@angular/core';
import {Http, Headers} from "@angular/http";
@Component({
    selector: 'app-root',
    templateUrl: './app.component.html',
    styleUrls: ['./app.component.css']
})

export class AppComponent {
    @ViewChild('request') requestElement: ElementRef;
    @ViewChild('response') responseElement: ElementRef;
    @ViewChild('error') errorElement: ElementRef;
    @ViewChild('price_of_bond_value') priceOfBondValueElement: ElementRef;

    // Initial values
    facevalueValue = 0;
    couponPaymentValue = 0;
    numPaymentsValue = 0;
    interestRateValue = 0;

    httpOptions = {
        headers: new Headers({
            'Content-Type': 'application/json',
            'Accept': 'application/json'
        })
    };

    constructor(private http: Http) {}

    // Calculate
    calculatePrice() {
        let url = "http://localhost:9910/BondTools/pricecalc";

        //Use MPS RESTful API to specify params using JSON
        let params = {
            "nargout": 1,
            "rhs": [this.facevalueValue, this.couponPaymentValue, this.interestRateValue, this.numPaymentsValue]
        };

        let requestElement = this.requestElement.nativeElement;
        requestElement.innerHTML = "URL: " + url + "<br>" + "Method: POST <br>" + "Data:" + JSON.stringify(params);


        const response = this.http.post(url, params, {headers: this.httpOptions.headers}).toPromise()
            .then(response => {
                //Use MPS RESTful API to check HTTP Status
                if (response.status === 200) {
                    // Deserialization: Converting text back into JSON object
                    // Response from server is deserialized 
                    let result = response.json();
                    //Use MPS RESTful API to retrieve response in "lhs"
                    if ('lhs' in result) {
                        let errorElement = this.errorElement.nativeElement;
                        errorElement.innerHTML = "";
                        let priceOfBondValueElement = this.priceOfBondValueElement.nativeElement;
                        priceOfBondValueElement.innerHTML = "Bond Price: " + result.lhs[0].mwdata;
                    } else {
                        let errorElement = this.errorElement.nativeElement;
                        errorElement.innerHTML = "Error: " + result.error.message;
                    }
                } else {
                    let errorElement = this.errorElement.nativeElement;
                    errorElement.innerHTML = "Error:" + response.statusText;
                }

                let responseElement = this.responseElement.nativeElement;
                responseElement.innerHTML = "Status: " + response.status + "<br>"
                    + "Status message: " + response.statusText + "<br>" +
                    "Response text: " + JSON.stringify(response.json());
        });
    }
}

書き直しは必要最小限にしたいと思いつつ、入力フォームやスライダーのところでngModleを使うとかなり実装が楽なので、かなり書き直してしまった。

実行結果

それではAngular-MaterialによるWebフロントでの実行結果を確認。

201866_213411.png

よしよし、入力フォームとスライダーがモダンになっていい感じ。
Webサーバーにデプロイする時はng build --prodでTypeScriptをビルドして、できたHTML/JavaScriptをWebサーバーに置き、MATLAB Production ServerもMATLAB上での「もどき」ではなく、ちゃんとパッケージ化してCTFファイルをMATLAB Production Serverに持っていって実行させれば良いのだが、Webフロントの開発を行う時はデバッグしやすい「もどき」が便利。

3
2
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
3
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?