2018年6月28日木曜日

MONACAサンプルアプリの編集2

今回は、グラフの描画部分のプログラミングを行う。具体的には、指定した国、年の人口を取得するPopulationサービスを書き換えて、OpenDolphinから指定した検査項目の検査結果を検査履歴から取得する処理を行う。
.service('Population', ['$http', function($http) {
  this.get = function(country, year) {
    return $http.jsonp('http://api.population.io:80/1.0/population/' + year + '/' + country + '/?format=jsonp&callback=JSON_CALLBACK')
      .then(
        function(response) {
          return response.data;
        }
      );
  };
}])
まず上記のコードを下記のコードに書き換える。
.service('Population', ['$http', function($http) {
  this.get = function(country, year) {
    return $http.get('http://172.16.108.251:8080/dolphin/openSource/lab/module/00001,0,6')
      .then(
        function(response) {
          alert(JSON.stringify(response.data,null,2));
          return response.data;
        }
      );
  };
}])
これは「$http」サービスを使ってOpenDolphinから検査データを取得するサービスPopulationである。また、確認の為6行目に、タップした際に検査データをアラートで表示する処理を加えている。編集を行った後、検査項目をタップすると、前回と同様の画面が表示された。

.service('Population', ['$http', function($http) {
  this.get = function(country, year) {
    return $http.get('http://172.16.108.251:8080/dolphin/openSource/lab/module/00001,0,6')
      .then(
        function(response) {
          //alert(JSON.stringify(response.data,null,2));
          var  laboResults = [];
          response.data.list.forEach(function(list){
            list.items.forEach(function(item){
              if(item.itemName == country){
                laboResults.push(item);
              }
            });
          });
          alert(JSON.stringify(laboResults,null,2));
          return laboResults;
        }
      );
  };
}])
次に、先ほどのコードを上記のコードに書き換える。
まず、7行目で配列変数laboResultsを定義し、その中身を空にする。そして、8行目はlist配列の各々に格納されている1日分の検査データ1つずつに対してループ処理を行っている。9行目は1日分の検査データ items の個々の検査項目1つずつに対してループ処理を行っている。10行目は選択したデータ(名称がcountryに格納されている)と同じ検査名称(itemName)の検査項目を抽出してlaboResults配列に追加するといった処理である。確認の為、スマートフォンアプリの方で検査項目「総ビリルビン」をタップしたところ、総ビリルビンのデータのみを表示させることが出来た。
図1 MONACAアプリ画面

2018年6月27日水曜日

MONACAサンプルアプリの編集1

前回前々回とMONACAのサンプルアプリのソースコードの解読を行い、おおまかではあるがある程度の構造を把握することが出来たので、今回は実際にサンプルアプリの編集を行う。
まず編集を行う際に、Wiresharkでのパケットキャプチャを利用し、どの様な通信方法が行われているのかを確認する。今回はあらかじめキャプチャされていたデータを用いる。尚、パケットキャプチャの閲覧を行う際にNotepad++というアプリケーションを利用する。Notepad++を実行し、パケットキャプチャを張り付けた後に、言語>J>JSONをクリックする事で、以下の図1の様に表示される。
図1 notepad++図1
1行目の
GET /dolphin/openSource/lab/module/00001,0,6
は検査データを最新から過去6日分までの取得を行う指示である。OpenDolphinで過去6日分ごとしかデータを表示することが出来なかったのはこのためであろう。
図の左に表示されている+ボタンをクリックする事で対応するタグに格納されているデータの開閉を行うことが出来る為、どのタグにどういったデータが存在しているのかを容易に認識することが出来る。下記図2は21行目から始まる「items」のデータを閉じた図である。
図2 notepad++図2

そして、もう一度+ボタンを押す事で、下記図3の様に再び「items」に格納されているデータの表示を行うことが出来る。
図3 notepad++図3
ここで、見覚えのあるデータ群の閲覧を行うことが出来、検査データは「items」の中に格納されていることを理解することが出来た。
データの格納場所が分かったところで、サンプルアプリの編集を行いたいのだが、OpenDolphinに接続するにはユーザIDとパスワードで認証を受ける必要がある。そのためには、図1から、HTTPヘッダにユーザIDとパスワードを設定すればよいことがわかる(4~5行目)。
ここでパスワードに設定した値であるが、MD5ハッシュ関数を用いてハッシュ値に変換している。これは、仮にハッシュ値に変換を行わずパスワードを送信した場合、パケットキャプチャを行うと筒抜けになってしまうためである。
実際に同じ数値に変換されるかどうかをMD5ハッシュ計算ツールを用いて確認を行った。すると、下記図4の様に表示され、同じ値であることを確認することが出来た。
図4 ハッシュ計算ツール図
ユーザIDとパスワードの記述方法について概ね理解することが出来たので、いよいよMONACAのサンプルアプリの編集を行う。尚、名前を「Onsen World Poplation」から「Open Dolphin Client」へ変更した。
まず、index.jsの「angular.module('app', ['onsen'])」の直下に
.config(['$httpProvider',function($httpProvider){  
  $httpProvider.defaults.headers.common.password='6f8e646f95af8e79096477e877245664';
  $httpProvider.defaults.headers.common.userName='1.3.6.1.4.1.9414.70.1:W3415020';
}])
を追加する。これは「$httpProvider」というAngularJS組み込みサービスを用いたものである。このサービスは同じくAngularJSの組み込みサービスである「$http」のデフォルトの動作を変更するために用いるサービスである。ここでは、「headers」属性を使って、HTTPリクエストのヘッダーにパスワードとユーザネームを書き込む処理を行っている。
これで、ヘッダへ設定すべき認証情報の書き込み処理は完了した。
次に、
.service('Countries', ['$http', function($http) {
  this.get = function() {
    return $http.get('countries.json')
      .then(
        function(response) {
          return response.data.countries;
        }
      );
  };
}])
これは国一覧のデータを取得し、リストへ送るサービスであるが、これを
.service('Countries', ['$http', function($http) {
  this.get = function() {
    return $http.get('http://172.16.108.251:8080/dolphin/openSource/lab/module/00001,0,6')
      .then(
        function(response) {
          alert(JSON.stringify(response.data.list[0],null,2));
          return response.data;
        }
      );
  };
}])
の様に書き換える。
1行目はサービスの宣言で、「Countries」はサービス名、次の配列内の最初の要素「$http」はこのサービスで使用する組み込みサービスで、次のfunctionがこのサービスのコンストラクタである。3行目の$http.getは引数に指定したOpenDolphinのURLから検査データの取得を行う。ここで指定するURLは図1の1行目に示されているものと、6行目のHost~に示されているIPアドレスを結合したものである。6行目では、OpenDolphinから取得したデータの内容を確認するためポップアップしている。また、確認のためだけに膨大なデータを出力するのは無駄であるため、list[0]として、最新の検査データのみを出力する。なお、JSON.stringify関数はJavascriptオブジェクトをJSON形式の文字列に変換する関数でである。
実際にスマートフォンアプリの方で確認したところ、図5の様に表示され、無事にデータの取得が行えていることを確認することが出来た。
図5 スマートフォンアプリ図1

次に、国名一覧を各検査項目名一覧に変更する。
Countries.get()
    .then(
      function(countries) {
        that.list = countries;
      }
    );
上記は、取得した国名をリストに送るといったコードであるが、これを下記のコードに書き換える。
Countries.get()
    .then(
      function(countries) {
        var list = countries.list[0];
        that.list = [];
        list.items.forEach(function(element){
          that.list.push(element.itemName);
        });
      }
    );
まず、4行目の記述で、ローカル変数「list」に「countries.list」に格納されている検査データのうち最新のものを代入し、5行目で、検査一覧を格納する属性「that.list」を空配列に初期化する。
そして6~7行目は、検査目「itemName」をそのリストの要素に追加するコードである。
このコードを保存し、スマートフォンアプリで確認すると下記の図6の様になり、無事に検査一覧を表示させることが出来た。
図6 スマートフォンアプリ図2

2018年6月20日水曜日

MONACAサンプルアプリのソースコード解読2

前回はMONACAのサンプルアプリであるチャートアプリの導入を行い、ソースコードの解読を試みた。今回は残りのソースコードの解読を試みる。

#index.html

<ons-template id="country.html">
        <ons-page ng-controller="CountryController as country">
          <ons-toolbar>
            <div class="left"><ons-back-button>Back</ons-back-button></div>
            <div class="center">{{ country.name }}</div>
          </ons-toolbar>

          <ons-list class="year-select-container">
            <ons-list-item>
              <select
                ng-model="country.year"
                ng-options="year for year in country.years"
                class="text-input text-input--transparent"
                style="width: 100%; margin-top: 4px">
              </select>
            </ons-list-item>
          </ons-list>

          <population-chart ng-if="country.showChart" data="country.population"></population-chart>
        </ons-page>
      </ons-template>
3行目の<ons-toolbar></ons-toolbar>でアプリのツールバーを作成している。4行目でツールバーの左側に「Back」ボタンを表示するようにして、5行目では、国名が中央に表示されるようにしている。
8行目の<ons-list class=”year-select-container”>はOnsen-UIでリスト(箇条書き)を作成するときに用いるコンポーネントである。リストの個々の項目は<ons-list-item>~</ons-list-item>の中に書く。それが10行目~14行目の<select>タグである。これは、人口をグラフ表示する際の西暦年を選択するための選択ボックスを定義している。<select>タグ内の属性ng-modelにはCountryController内で定義しているコントローラのプロパティcountry.yearを設定しているが、これが選択ボックスのデフォルト値になる。ちなみにコントローラ内でこのプロパティ(this.year)には現在の西暦年が設定されている。
ng-optionsは選択ボックスのオプション(選択肢)を生成するAngularJSのディレクティブで、”year for year in country.years”が、コントローラのプロパティcountry.years配列から西暦年を取り出して<option>タグを生成している。ちなみに、”year for year in country.years”は、country.years配列から一つずつ配列要素を取り出してきてそれをyearに設定し、そのyearを使って<option value="year">year</select>タグを生成するといった意味である。
19行目の<population-chart></population-chart>は、本アプリで独自にAngularJSのdirectiveメソッドを用いて作成しているタグ(下記index.jsの132~181行目)で、nvd3.jsライブラリを用いてグラフを作成するように定義されている。AngularJSのdirectiveメソッドはHTMLに独自のタグを追加する機能があり、これによってHTMLの拡張が可能になる。19行目だけでグラフを作っているのは驚くべきことである。

#index.js

angular.module('app', ['onsen'])

.controller('CountriesController', ['$scope', 'Countries', function($scope, Countries) {
  var that = this;

  Countries.get()
    .then(
      function(countries) {
        that.list = countries;
      }
    );

  this.showCountry = function(country) {
    //$scope.navi.pushPage('country.html', {data: {name: country}});
    navi.pushPage('country.html', {data: {name: country}});
  };
}])

.controller('CountryController', ['$scope', 'Population', function($scope, Population) {
  var that = this;
  this.name = $scope.navi.topPage.data.name;

  this.showChart = false;
  $scope.navi.on('postpush', function() {
    $scope.$evalAsync(function() {
      that.showChart = true;
    });

    $scope.navi.off('postpush');
  });

  var currentYear = (new Date()).getUTCFullYear();
  this.year = currentYear + '';

  this.years = [];
  for (var i = 1950; i <= 2100; i++) {
    this.years.push('' + i);
  }

  this.getPopulation = function() {
    Population.get(this.name, this.year)
      .then(
        function(population) {
          that.population = population;
        }
      );
  };

  $scope.$watch('country.year', function() {
    that.getPopulation();
  });
}])

.service('Countries', ['$http', function($http) {
  this.get = function() {
    return $http.get('countries.json')
      .then(
        function(response) {
          return response.data.countries;
        }
      );
  };
}])

.service('Population', ['$http', function($http) {
  this.get = function(country, year) {
    return $http.jsonp('http://api.population.io:80/1.0/population/' + year + '/' + country + '/?format=jsonp&callback=JSON_CALLBACK')
      .then(
        function(response) {
          return response.data;
        }
      );
  };
}])

.factory('nv', function() {
  return nv;
})

.factory('d3', function() {
  return d3;
})

.controller('PopulationChartController', ['$scope', function($scope) {
  $scope.formatData = function() {
    if (!$scope.data) {
      return [];
    }

    var total = [],
      males = [],
      females =[];

    for (var i = 0; i < $scope.data.length; i++) {
      var data = $scope.data[i];

      total.push({x: data.age, y: data.total});
      males.push({x: data.age, y: data.males});
      females.push({x: data.age, y: data.females});
    }

    return [
      {
        key: 'Total',
        values: total
      },
      {
        key: 'Women',
        values: females
      },
      {
        key: 'Men',
        values: males
      }
    ];
  };


  $scope.$on('chartloaded', function(event) {
    event.stopPropagation();

    $scope.updateChart($scope.formatData());

    $scope.$watch('data', function() {
      if ($scope.data) {
        $scope.updateChart($scope.formatData());
      }
    });
  });
}])

.directive('populationChart', ['nv', 'd3', function(nv, d3) {
  return {
    restrict: 'E',
    scope: {
      data: '='
    },
    controller: 'PopulationChartController',
    link: function(scope, element, attrs) {
      var svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
      element.append(svg);

      var chart;

      scope.updateChart = function(data) {
        d3.select(svg)
          .datum(data)
          .call(chart);
      };

      nv.addGraph(function() {
        chart = nv.models.stackedAreaChart()
        .useInteractiveGuideline(true)
        .showControls(false)
        .margin({left: 50, right: 30});

        chart.xAxis
          .tickFormat(d3.format(',r'));

        chart.yAxis
          .tickFormat(function(d) {
            if ((d / 1000000) >= 1) {
              d = Math.round(d / 100000) / 10 + 'M';
            }
            else if ((d / 1000) >= 1) {
              d = Math.round(d / 100) / 10 + 'K';
            }
            return d;
          });

        nv.utils.windowResize(function() {
          chart.update();
        });

        scope.$emit('chartloaded');

        return chart;
      });
    }
  }
}]);
19行目~52行目はコントローラCountryControllerの定義である。先に見た西暦年選択ボックス作成のために必要となるデータが定義されている。32行目でcurrentYearに現時点の西暦年を4桁で設定している。
33行目はyearにcurrentYearの値に空文字('')を結合することで、数値型を文字列型に変換している。
35行目でyearsを空の配列に初期化し、36行目~38行目のfor文でiを1950~2100の範囲で1ずつ増やしながらyears配列に追加(push)している。こうして西暦年配列yearsが作られ、先に見た<select>タグのng-options属性で<option>タグを生成するのに用いられている。このようにAngularJSのコントローラとHTMLのng-で始まるディレクティブは相互に関連付けられている。

132行目はdirectiveメソッドを用いて、populatationChartというタグ(カスタムディレクティブ)の定義を行っており、この中でNVD3というグラフィックライブラリを用いてグラフを生成している。

133行目にあるように、directiveメソッドの本体部分は、return命令で133行目~180行目に定義されたオブジェクトを返すだけの処理からなっている。
では、このreturn命令がどのようなオブジェクトを返しているかを見てみよう。
まず、134行目のrestrictは、HTMLの中でこのディレクティブをどのように使うかを指定する。「E」はタグ(コンポーネント)として使うことを指定している。
135行目~137行目のscopeは、ディレクティブに独自のスコープを定義するためのものである。136行目のdata:'='は、HTML内に書いたdata属性を、ディレクティブ内のdata属性と同じにすることを指定している(双方向バインディング)。
138行目は、このディレクティブを「PopulationChartController」というコントローラと連携させることを意味している。このディレクティブで定義したタグ内の属性で「ng-controller="PopulationChartController"」と記述するのと同義である。
そのPopulationChartControllerはHTMLのdata属性に指定されたデータを用いてグラフィックライブラリNVD3が使うデータフォーマットに変換することを主な機能とするコントローラで、84行目~116行目にかけて定義されている。

139行目のlinkはディレクティブにおいて一番重要な部分であり、タグの役割、機能を定義している。functionという記述から、関数として定義されている。HTMLでPopulationChartを呼び出すと、この関数が実行される。この関数を実行すると139行目から179行まで実行され、return chartで結果をグラフで返す処理を行う。
140行目のdocument.createElementNSというのは、HTMLのタグ(DOMという)を動的に作っているというものである。ここではsvgタグというものを作っている。svgタグというものは、グラフを作るためのXMLタグであり、グラフを描画するときには必ず使うものである。
145行目~149行目はUpdateChart関数の定義であり、グラフを更新する際にこの関数が呼び出される。
146行目でsvgタグの選択を行い、147行目でデータを与えて、148行目でチャート(グラフ)を生成している。
151行目以降はグラフの追加を行っている記述であるのだが、少々小難しく、理解しづらかったため、似たようなサンプルプログラムを活用し、理解を深めていく。

#NVD3.jsを使ってみるのサンプルデータから

<link href="https://cdn.rawgit.com/novus/nvd3/v1.8.1/build/nv.d3.css" rel="stylesheet" type="text/css">
<body>
    <div id="chart">
        <svg style="height:500px;" />
    </div>

    <script src="https://d3js.org/d3.v3.min.js"></script>
    <script src="https://cdn.rawgit.com/novus/nvd3/v1.8.1/build/nv.d3.js"></script>
    <script type="text/javascript">

    nv.addGraph(function() {
        //var chart = nv.models.historicalBarChart();
        //var chart = nv.models.lineWithFocusChart();
        var chart = nv.models.lineChart();

        chart.xAxis.tickFormat(d3.format(',f'));
        chart.yAxis.tickFormat(d3.format(',.2f'));

        d3.select('#chart svg')
            .datum(testData())
            .transition().duration(500)
            .call(chart);

        nv.utils.windowResize(chart.update);

        return chart;
    });

    function testData() {
        return [{color:'red',
                 key:'hoge',
                 values: [{x:1,y:100},
                          {x:2,y:130},
                          {x:3,y:90},]},
                {color:'blue',
                 key:'hage',
                 values: [{x:1,y:70},
                          {x:2,y:60},
                          {x:3,y:80},
                          {x:4,y:90}]}];
    }
    </script>
</body>
4行目は、グラフィックライブラリNVD3でグラフを描画するのに使うsvgタグである。
11行目~27行目がグラフを書いている本体である。
16行目はグラフのx軸の目盛りのフォーマット指定部分である。ここでは実数形式にすることを指定している
17行目はグラフのy軸の目盛りフォーマット指定である。ここでは、小数点以下2桁まで表示するように指定している。
19行目~23行目は4行目のsvgタグにテストデータを入れ、チャートを作っている部分である。
19行目はスタイルシートで使うセレクタを使って4行目のsvgタグをグラフを埋め込むタグとして選択している。セレクタでは接頭語の#はidを意味するので、セレクタ'#chart svg'は、idがchartのタグ(すなわち3行目のdivタグ)の中にあるsvgタグ(すなわち4行目)を表している。
20行目は29行目~41行目で定義されたテストデータをグラフ描画の対象データに設定している。
22行目はグラフの描画を行っている。
先にも書いたように、29行目~41行目は折れ線グラフのデータを定義している。データは2系列あり、配列要素として格納されている。各系列のデータにはcolor属性、key属性、values属性があり、それぞれグラフの色、系列名、グラフを構成する(x, y)座標の配列を表している。
NVD3でグラフを作成する場合はこのフォーマットに変換すればよいことがわかる。
この観点でアプリのソースコードを眺めると、index.jsのPopulationChartControllerでNVD3のデータを作成しているのがわかる。90行目~92行目にかけて、total, males, femalesの3つの配列を定義し、94行目~100行目のfor文でdata属性のageをx座標に、人数(total, males, females)をy座標にした点オブジェクトを各々の配列に追加(push)している。そして、最後に102行目~116行目でNVD3のフォーマット(key属性とvalues属性)にしてreturnしている。こうして3系列(合計、男性、女性の人口)のデータを作成していることがわかる。

#NVD3を使って、AngularJSで作ったハイブリッドアプリのグラフを統合しようより

HTML

<div ng-app="app" ng-controller="MyController as main">
  <line-chart height="250px" data="main.data"></line-chart>

  <h3>Change the values: </h3>

  <p ng-repeat="datum in main.data[0].values">
    <input type="number" ng-model="datum.y">
  </p>
</div>

JavaScript

angular.module('app', [])
  .controller('MyController', ['$scope', function($scope) {
    this.data = [{
      key: 'Data',
      values: [{
        x: 0,
        y: 0
      }, {
        x: 1,
        y: 1
      }, {
        x: 2,
        y: 4
      }, {
        x: 3,
        y: 9
      }, {
        x: 4,
        y: 16
      }, {
        x: 5,
        y: 25
      }]
    }];
  }]);

HTML(上図)の2行目にある<line-char>タグは、AngularJSのディレクティブを使って定義したグラフ作成のためのタグである。その属性にdata=”main.data”とあるが、これはJavascript(下図)の中にあるAngularJSのコントローラ"MyController"の3行目で定義されたdata属性で、この中には3行目~24行目にかけて定義されたデータが格納されている。

2018年6月14日木曜日

MONACAサンプルアプリの導入と、ソースコードの解読1

今回は、MONACAを用いてアプリケーションの開発を試みるのだが、一から作成するといった作業は時間的に余裕がありそうにないので、あらかじめ用意されているテンプレートの編集を行い、アプリケーションの開発を試みる。
まず、MONACA Docsに接続し、サンプルアプリをクリックする。すると、サンプルアプリケーション一覧が表示される。今回作成したいアプリケーションはグラフの描画機能を持っているものなので、「チャートアプリ」の導入を行った。

図1 サンプルアプリ一覧
導入を行う際は、上記図1のアプリ図に示されている「Direct Import」をクリックする事で、MONACAにインポートすることができる。また、インポートを行う際は、下記の図2のような確認ダイアログが表示される。
図2 サンプルアプリインポートダイアログ
インポートを行い、MONACAのダッシュボードに移動すると、下記図3の様に無事にインポートされていることが分かる。尚、MONACAは無料試遊版だとプロジェクトを3つまでしか保有することが出来ないため、注意が必要である。
図3 MONACAダッシュボード
仕様の確認を行うため、スマートフォン版でアプリケーションの確認を行ったのだが、
下記図4のようなポップアップが表示された。
図4 MONACAアプリケーション図1
これは,アンプルアプリのCordovaのバージョンが古いため、表示された警告メッセージである。そこで、一旦ブラウザ版の方に戻り、「onsen World Poplation」を「クラウドIDEで開く」で編集画面に遷移する。そして、設定タブから「Cordova プラグインの管理」をクリックする。
図5 クラウドIDE図1
すると、上記図5のような画面が表示される。どうやら最新バージョン「7.1.0」に対してこちらのバージョンは「6.5.0」であったため、エラーが表示されたようである。「7.1.0にアップグレード」をクリックすると、下記図6のような画面が表示され、
図6 クラウドIDE図2
OKをクリックする事で、図7のような画面が表示され、無事にアップデートを行う事ができた。
図7 クラウドIDE図3
無事にアップデートを行う事が出来たので、次はスマートフォンでアプリケーションの大まかな仕様について確認を行った。
図8 MONACAアプリケーション図2
まずアプリケーションを実行すると、上記図8の様な画面が表示された。
これは各国毎にデータを分けて格納されていることが分かる。
今回は「Japan」のサンプルデータの確認を行ってみた。
図9 MONACAアプリケーション図3
「Japan」をタップすると、上記図9のような図が表示された。とあるデータを参照し、この様なグラフの描画を行っているのだろう。また青色のタブをタップすることで、図10の様な年別でのデータ表示の切り替えも行えることが出来る。
図10 MONACAアプリケーション図4
これでおおよその仕様の確認を行う事が出来た。次にこのアプリケーションのソースコードの解読を行い、どのデータを書き換えれば、我々が作成したいアプリケーションの開発が行えるかどうかを試みる。
このアプリケーションを形成しているものは主に「index.html」,「index.js」,「countries.json」の3つである。また、今回は実際に解読を行った部分のみを抜粋して記載している。

#index.html

<body>
 <ons-navigator page="main.html" var="navi"></ons-navigator>

 <ons-template id="main.html">
  <ons-page ng-controller="CountriesController as countries">
   <ons-toolbar>
    <div class="center">World Population</div>
   </ons-toolbar>

   <div class="filter-input-container">
    <input class="search-input" ng-model="query" placeholder="Filter countries" type="search" value="" />
   </div>
   <ons-list>
    <ons-list-item modifier="chevron" ng-click="countries.showCountry(country)" ng-repeat="country in countries.list | filter:query">
     {{ country }}
    </ons-list-item>
   </ons-list>
  </ons-page>
 </ons-template>

 <ons-template id="country.html">
  <ons-page ng-controller="CountryController as country">
   <ons-toolbar>
    <div class="left">
     <ons-back-button>Back</ons-back-button>
    </div>
    <div class="center">
     {{ country.name }}
    </div>
   </ons-toolbar>

   <ons-list class="year-select-container">
    <ons-list-item>
     <select class="text-input text-input--transparent" ng-model="country.year" ng-options="year for year in country.years" style="margin-top: 4px; width: 100%;">
     </select>
    </ons-list-item>
   </ons-list>

   <population-chart data="country.population" ng-if="country.showChart"></population-chart>
  </ons-page>
 </ons-template>
</body>
1行目の<body></body>の間にあるHTMLがアプリが表示する画面(View)を定義している。2行目~40行目にかけて多く見られる<ons-…>の「ons」は「Onsen-UI」というモバイルアプリの開発に特化したUIコンポーネントである。<ons-page>で囲まれたページの中に、他のHTML要素やOnsen UIコンポーネントを記述してユーザインターフェースを開発する。
<ons-navigator…>は、Onsen UIのサイトの記載によると、ページの切り替え機能を提供するコンポーネントだとわかる。
<ons-template…>は、これもOnsen UIのサイトの記載によると、id属性に指定した名前をpageのURLとしてons-navigatorなどのコンポーネントから参照できるHTMLを定義するものである。これを使うと個々の画面のHTMLファイルを別々に分けなくとも1つのファイル内に記述できるようになる。
この機能を使って、4行目~20行目、22行目~40行目の<ons-template…></ons-template>はそれぞれで独立したページの指定を行っている。
4行目~20行目は「main.html」のファイル、22行目~40行目は「country.html」のファイルを指定している。
15行目にある「…ng-repeat="country in countries.list…」は、AngularJSのCountriesController(国名を管理するコントローラ)からcountry(国名)のデータを繰り返し取得し、16行目の{{ country }}によって、アプリ画面に国名が表示されるようにしている。

#index.js

angular.module('app', ['onsen'])
.controller('CountriesController', ['$scope', 'Countries', function($scope, Countries) {
  var that = this;
  Countries.get()
    .then(
      function(countries) {
        that.list = countries;
      }
    );
  this.showCountry = function(country) {
    $scope.navi.pushPage('country.html', {data: {name: country}});
  };
}])
.service('Countries', ['$http', function($http) {
  this.get = function() {
    return $http.get('countries.json')
      .then(
        function(response) {
          return response.data.countries;
        }
      );
  };
}])
.service('Population', ['$http', function($http) {
  this.get = function(country, year) {
    return $http.jsonp('http://api.population.io:80/1.0/population/' + year + '/' + country + '/?format=jsonp&callback=JSON_CALLBACK')
      .then(
        function(response) {
          return response.data;
        }
      );
  };
}])
MonacaはAngulaJSというJavaScriptのフレームワークを用いている。1行目はAngulaJSのモジュールを定義しているところである。モジュールはAngularJSの最も基本的な単位で、アプリを構成する様々な部品(コントローラ、サービス、ディレクティブなど)をモジュールの下に作成していく。
2行目は「CountriesController」という名前のコントローラメソッドの記述である。$scopeというのは、 ビュー(アプリの画面のこと:すなわちHTML5で作成する画面)にデータなどを渡したり、ビューから発生したイベントを監視するなど、ビューとのやり取りを行うことができる特別なオブジェクトである。具体的には、このコントローラが参照されるHTMLノード(この場合はmain.htmlのons-pageノード)自身を表している。
3行目では、$scopeを意味するthisを一時変数thatに代入している(いろいろな問題を回避するため、$scopeを使わずthisを利用するようになっているらしい)。
4行目~9行目はサービスCountries(14~23行目)からデータ(国名一覧:countries.json)の取得を行い、うまくいった場合は自身のリストにデータを流し込むといった処理を行っている。
10行目~12行目は、引数に指定されたcountryの詳細ページを表示すべく、country.htmlへの画面遷移を行うメソッドを定義している。this.showCountryのようにthisで修飾することで、このコントローラのスコープ内で利用できるメソッドを定義できる。
14行目~22行目は、JSON形式の国名一覧「countires.jon」からデータを取得するサービスメソッドCountriesの定義である。このサービスはCountriesControllerのサービスとしてコントローラに注入されて利用されている(2行目)。
24行~33行は、「Population」というサービスメソッドの定義である。引数に指定した国及び年(それぞれcountryおよびyear)の人口データを取得すべく、26行目で
http://api.population.io:80/1.0/population/' + year + '/' + country + '/?format=jsonp&callback=JSON_CALLBACK
というURLを作成し、Webサービスを提供するサーバに人口データの要求を行っている。
ここで、試しに2018年の日本の人口データをブラウザから要求してみた。手作業で作成したURLは次のとおりである。
http://api.population.io/1.0/population/2018/Japan?format=jsonp
すると、以下の様なJSON形式の人口データが返ってくることを確認できた。
[
  {
    "females": 493727,
    "country": "Japan",
    "age": 0,
    "males": 520667,
    "year": 2018,
    "total": 1014404
  }
今回解読したソースコードをおおまかに整理すると、以下の図11のようになる。
図11 index.jsフロー図
まず、controller「CountryController」に注入したservice「Countries」のgetメソッドを使ってcountries.jsonから国名データを取得する。その後、取得したデータをcontrollerのlist属性に流しこむといった具合だ。
今回はサンプルアプリの導入と、簡単なプログラムの分析を行った。次回は解析しきれていないソースコードを分析し、目的のスマホアプリを開発するにはどの部分の書き換えを行うべきかを見定めていきたい。


2018年6月7日木曜日

スマホアプリを作成する為のMONACAのアカウント登録と試食

今回はスマートフォンアプリを作成する為にMONACAというアプリ開発クラウドを利用した。まずログイン時に以下の図1のようなダッシュボードが表示される。
図1 MONACAダッシュボード画面
また、編集内容が反映されているかどうかを確認するため、スマートフォンアプリの方も導入した。
図2 スマホ版MONACA図1
ログインをすると、図2のような画面が表示された。
次に右下のリングメニューをタップすると、図3のようなメニューが表示される。
図3 リングメニュー一覧
リングメニューとしてはそれぞれ
1,ダッシュボード一覧に戻る
2,リロード
3,スクリーンショットの保存
4,検索?
5,ログの表示
6,リングメニューを閉じる
尚、4に関してはグレーアウトしており、タップすることができなかったのであくまで憶測である。
次にアプリケーションの編集を行うため、WEB版に戻り、はじめてのMONACAアプリ「はじめてのMONACAアプリ」というアイコンをクリックすると右に編集一覧が表示される。今回は図4の様に「クラウドIDEで開く」というアイコンをクリックする。
図4 はじめてのMONACAアプリを開いた状態
クラウドIDEを開くをクリックすると、以下の図5の様な画面が表示された。
図5 クラウドIDEで開いた図
クラウドIDEを開いた後左タブのwww>phongape-demo>index.html画面をクリックする事で、indexの編集を行う事ができる。
今回は試しに、図5の20行目に赤線で示されている「Hello Word!」といった文面を変更する。変更を行った後はメニュータブから保存をクリックする事で、編集を保存することができる。
図6 編集後のindex図
保存を行った後、スマートフォンアプリの方で確認をしたところ、図6の様の変更されていることを確認することができた。
次にボタンを押した際に「終了します」といったポップアップが表示されるようなボタンの追加を行う。
図7 ボタン追加図
再びWEBMONACAの方に戻り、23行目に下記のプログラム行を追加した。
また、一つ目のボタンと間をあけるために22行目に「br」を追加する。
<a class="button--large" href="https://www.blogger.com/null" onclick="alert('終了します')">Stop Demo</a>
図8 Stop Demoをタップした状態
編集を行い、保存をした後またスマートフォンアプリの方で確認をしたところ、Start Demoの下にStop Demoといったボタンが表示され、タップすることで、図8のようなポップアップメニューが表示された。
今回はMONACAの表面上の部分だけを体験する事ができたので、次回はMONACAのチュートリアルを確認しながら、具体的な動作の確認、習得を行いたい。

2018年6月6日水曜日

2.受付画面

本日のゼミでは、前回同様にOpenDolphinのパケットをキャプチャし、実際にどのようなデータが流れているのかを確認した。

今回確認を行ったOpenDolphinのパケットは、「受付画面」→「患者選択」→「カルテ画面」に至るまでのパケットである。(図1,2)


図1.OpenDolphinの受付画面
図2.OpenDolphinのカルテ画面

wiresharkを起動させ、キャプチャフィルタを「host 172.16.108.251」に設定して(図3)、キャプチャを開始し、上記に書いた「受付画面」→「患者選択」→「カルテ画面」を行いパケットをキャプチャした。
図3.キャプチャフィルタの設定

その後キャプチャを終了し、キャプチャを確認した結果は図4の通りである。
図4.パケットキャプチャの結果
その後、HTTP~GET(またはPUT)の
キャプチャを追跡→HTTPストリームの確認する。
文字コードをUTF-8に変更し、json部分はJSON Pretty Printより整形をする。
今回は以下の4つのパケットがキャプチャできた。


#1 GET /dolphin/openSource/karte/31,2008-06-06%2013:00:00 HTTP/1.1



GET /dolphin/openSource/karte/31,2008-06-06%2013:00:00 HTTP/1.1
Accept: application/json
Accept-Encoding: gzip, deflate
password: 6f8e646f95af8e79096477e877245664
userName: 1.3.6.1.4.1.9414.70.1:w3415038
Host: 172.16.108.251:8080
Connection: Keep-Alive

HTTP/1.1 200 OK
Connection: keep-alive
X-Powered-By: Undertow/1
Server: WildFly/9
Content-Type: application/json
Content-Length: 2102
Date: Wed, 06 Jun 2018 04:01:40 GMT

{
  "lastDocDate": 1526539476658,
  "created": "2018-05-17",
  "allergies": null,
  "heights": null,
  "weights": null,
  "patientVisits": [
    "2018-05-17T15:22:21"
  ],
  "docInfoList": [
    {
      "departmentCodeSys": null,
      "docPk": 79,
      "purposeDesc": null,
      "purposeCodeSys": null,
      "healthInsuranceCodeSys": null,
      "versionNotes": null,
      "parentId": null,
      "parentIdDesc": null,
      "parentIdCodeSys": null,
      "labtestOrderNumber": null,
      "pvthealthInsuranceModel": null,
      "createrLisence": null,
      "title": "嘔吐・下痢の症状がみられる",
      "departmentDesc": "内科,01,〇〇 〇〇,10004,JPN000000000000",
      "firstConfirmDate": 1526539476658,
      "confirmDate": 1526539476658,
      "facilityName": null,
      "patientName": null,
      "patientId": null,
      "department": "01",
      "parentPk": 0,
      "docId": "6cd5ea01ac10667f015ec41706ccae36",
      "sendClaim": false,
      "parentIdRelation": null,
      "docType": "karte",
      "healthInsurance": "06",
      "healthInsuranceDesc": "06 組合",
      "healthInsuranceGUID": "5b945b2f-5388-4483-b9aa-097d02ad952a",
      "claimDate": 1526539466036,
      "purpose": "recode",
      "patientGender": null,
      "versionNumber": "1.0",
      "hasTreatment": false,
      "hasLaboTest": false,
      "sendLabtest": false,
      "sendMml": false,
      "hasRp": true,
      "hasImage": false,
      "hasMark": false,
      "status": "F"
    },
    {
      "departmentCodeSys": null,
      "docPk": 48,
      "purposeDesc": null,
      "purposeCodeSys": null,
      "healthInsuranceCodeSys": null,
      "versionNotes": null,
      "parentId": null,
      "parentIdDesc": null,
      "parentIdCodeSys": null,
      "labtestOrderNumber": null,
      "pvthealthInsuranceModel": null,
      "createrLisence": null,
      "title": "風邪初診",
      "departmentDesc": "内科,01,〇〇 〇〇,10004,JPN000000000000",
      "firstConfirmDate": 1526538665611,
      "confirmDate": 1526538665611,
      "facilityName": null,
      "patientName": null,
      "patientId": null,
      "department": "01",
      "parentPk": 0,
      "docId": "6cc5df93ac10667f014541e8efd2397f",
      "sendClaim": false,
      "parentIdRelation": null,
      "docType": "karte",
      "healthInsurance": "06",
      "healthInsuranceDesc": "06 組合",
      "healthInsuranceGUID": "5b945b2f-5388-4483-b9aa-097d02ad952a",
      "claimDate": 1526538643879,
      "purpose": "recode",
      "patientGender": null,
      "versionNumber": "1.0",
      "hasTreatment": false,
      "hasLaboTest": false,
      "sendLabtest": false,
      "sendMml": false,
      "hasRp": true,
      "hasImage": false,
      "hasMark": false,
      "status": "F"
    }
  ],
  "memoList": null,
  "id": 33
}


#2 PUT /dolphin/openSource/chartEvent/event HTTP/1.1
PUT /dolphin/openSource/chartEvent/event HTTP/1.1
Accept-Encoding: gzip, deflate
Content-Type: application/json
password: 6f8e646f95af8e79096477e877245664
userName: 1.3.6.1.4.1.9414.70.1:w3415038
Content-Length: 298
Host: 172.16.108.251:8080
Connection: Keep-Alive

{
  "state": 1,
  "byomeiCountToday": 0,
  "patientModel": null,
  "ownerUUID": "w3415038:eacac34c-c8e7-4bd8-9e71-35f3c3653ddc",
  "memo": null,
  "facilityId": "1.3.6.1.4.1.9414.70.1",
  "byomeiCount": 0,
  "ptPk": 31,
  "pvtPk": 34,
  "patientVisitModel": null,
  "issuerUUID": "w3415038:eacac34c-c8e7-4bd8-9e71-35f3c3653ddc",
  "eventType": 0
}

HTTP/1.1 200 OK
Connection: keep-alive
X-Powered-By: Undertow/1
Server: WildFly/9
Content-Type: application/json
Content-Length: 1
Date: Wed, 06 Jun 2018 04:01:41 GMT

1

#3 GET /dolphin/openSource/karte/documents/79 HTTP/1.1
GET /dolphin/openSource/karte/documents/79 HTTP/1.1
Accept: application/json
Accept-Encoding: gzip, deflate
password: 6f8e646f95af8e79096477e877245664
userName: 1.3.6.1.4.1.9414.70.1:w3415038
Host: 172.16.108.251:8080
Connection: Keep-Alive

HTTP/1.1 200 OK
Connection: keep-alive
X-Powered-By: Undertow/1
Server: WildFly/9
Content-Type: application/json
Content-Length: 10286
Date: Wed, 06 Jun 2018 04:01:42 GMT

{
  "list": [
    {
      "linkId": 0,
      "linkRelation": null,
      "recorded": 1526539476658,
      "userModel": {
        "userId": null,
        "facilityModel": null,
        "sirName": null,
        "givenName": null,
        "commonName": "〇〇 〇〇",
        "departmentModel": null,
        "memo": null,
        "email": null,
        "orcaId": null,
        "useDrugId": null,
        "memberType": null,
        "registeredDate": null,
        "licenseModel": null,
        "roles": null,
        "password": null,
        "id": 13
      },
      "karteBean": {
        "lastDocDate": null,
        "created": null,
        "allergies": null,
        "heights": null,
        "weights": null,
        "patientVisits": null,
        "docInfoList": null,
        "memoList": null,
        "id": 33
      },
      "started": 1526539476658,
      "ended": null,
      "docInfoModel": {
        "departmentCodeSys": null,
        "docPk": 79,
        "purposeDesc": null,
        "purposeCodeSys": null,
        "healthInsuranceCodeSys": null,
        "versionNotes": null,
        "parentId": null,
        "parentIdDesc": null,
        "parentIdCodeSys": null,
        "labtestOrderNumber": null,
        "pvthealthInsuranceModel": null,
        "createrLisence": null,
        "title": "嘔吐・下痢の症状がみられる",
        "departmentDesc": "内科,01,〇〇 〇〇,10004,JPN000000000000",
        "firstConfirmDate": 1526539476658,
        "confirmDate": 1526539476658,
        "facilityName": null,
        "patientName": null,
        "patientId": null,
        "department": "01",
        "parentPk": 0,
        "docId": "6cd5ea01ac10667f015ec41706ccae36",
        "sendClaim": false,
        "parentIdRelation": null,
        "docType": "karte",
        "healthInsurance": "06",
        "healthInsuranceDesc": "06 組合",
        "healthInsuranceGUID": "5b945b2f-5388-4483-b9aa-097d02ad952a",
        "claimDate": 1526539466036,
        "purpose": "recode",
        "patientGender": null,
        "versionNumber": "1.0",
        "hasTreatment": false,
        "hasLaboTest": false,
        "sendLabtest": false,
        "sendMml": false,
        "hasRp": true,
        "hasImage": false,
        "hasMark": false,
        "status": "F"
      },
      "confirmed": 1526539476658,
      "modules": [
        {
          "beanBytes": "PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPGphdmEgdmVyc2lvbj0iMS44LjBfMTYxIiBjbGFzcz0iamF2YS5iZWFucy5YTUxEZWNvZGVyIj4KIDxvYmplY3QgY2xhc3M9Im9wZW4uZG9scGhpbi5pbmZvbW9kZWwuUHJvZ3Jlc3NDb3Vyc2UiPgogIDx2b2lkIHByb3BlcnR5PSJmcmVlVGV4dCI+CiAgIDxzdHJpbmc+Jmx0O3NlY3Rpb24gc3RhcnQ9JnF1b3Q7MCZxdW90OyBlbmQ9JnF1b3Q7MTQmcXVvdDsmZ3Q7Jmx0O3BhcmFncmFwaCBzdGFydD0mcXVvdDswJnF1b3Q7IGVuZD0mcXVvdDsxNCZxdW90OyZndDsmbHQ7Y29udGVudCBzdGFydD0mcXVvdDswJnF1b3Q7IGVuZD0mcXVvdDsxMyZxdW90OyBuYW1lPSZxdW90O2NvbnRlbnQmcXVvdDsmZ3Q7Jmx0O3RleHQmZ3Q75ZiU5ZCQ44O75LiL55ei44Gu55eH54q244GM44G/44KJ44KM44KLJmx0Oy90ZXh0Jmd0OyZsdDsvY29udGVudCZndDsmbHQ7Y29udGVudCBzdGFydD0mcXVvdDsxMyZxdW90OyBlbmQ9JnF1b3Q7MTQmcXVvdDsmZ3Q7Jmx0O3RleHQmZ3Q7CiZsdDsvdGV4dCZndDsmbHQ7L2NvbnRlbnQmZ3Q7Jmx0Oy9wYXJhZ3JhcGgmZ3Q7Jmx0Oy9zZWN0aW9uJmd0Ozwvc3RyaW5nPgogIDwvdm9pZD4KIDwvb2JqZWN0Pgo8L2phdmE+Cg==",
          "linkId": 0,
          "linkRelation": null,
          "recorded": 1526539476658,
          "userModel": {
            "userId": null,
            "facilityModel": null,
            "sirName": null,
            "givenName": null,
            "commonName": null,
            "departmentModel": null,
            "memo": null,
            "email": null,
            "orcaId": null,
            "useDrugId": null,
            "memberType": null,
            "registeredDate": null,
            "licenseModel": null,
            "roles": null,
            "password": null,
            "id": 13
          },
          "karteBean": {
            "lastDocDate": null,
            "created": null,
            "allergies": null,
            "heights": null,
            "weights": null,
            "patientVisits": null,
            "docInfoList": null,
            "memoList": null,
            "id": 33
          },
          "started": 1526539476658,
          "ended": null,
          "moduleInfoBean": {
            "stampNumber": 0,
            "stampName": "progressCourse",
            "stampRole": "soaSpec",
            "entity": "progressCourse"
          },
          "confirmed": 1526539476658,
          "status": "F",
          "id": 80
        },
        {
          "beanBytes": "PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPGphdmEgdmVyc2lvbj0iMS44LjBfMTYxIiBjbGFzcz0iamF2YS5iZWFucy5YTUxEZWNvZGVyIj4KIDxvYmplY3QgY2xhc3M9Im9wZW4uZG9scGhpbi5pbmZvbW9kZWwuQnVuZGxlRG9scGhpbiI+CiAgPHZvaWQgcHJvcGVydHk9ImJ1bmRsZU51bWJlciI+CiAgIDxzdHJpbmc+MTwvc3RyaW5nPgogIDwvdm9pZD4KICA8dm9pZCBwcm9wZXJ0eT0iY2xhaW1JdGVtIj4KICAgPGFycmF5IGNsYXNzPSJvcGVuLmRvbHBoaW4uaW5mb21vZGVsLkNsYWltSXRlbSIgbGVuZ3RoPSIxIj4KICAgIDx2b2lkIGluZGV4PSIwIj4KICAgICA8b2JqZWN0IGNsYXNzPSJvcGVuLmRvbHBoaW4uaW5mb21vZGVsLkNsYWltSXRlbSI+CiAgICAgIDx2b2lkIHByb3BlcnR5PSJjbGFzc0NvZGUiPgogICAgICAgPHN0cmluZz4wPC9zdHJpbmc+CiAgICAgIDwvdm9pZD4KICAgICAgPHZvaWQgcHJvcGVydHk9ImNsYXNzQ29kZVN5c3RlbSI+CiAgICAgICA8c3RyaW5nPkNsYWltMDAzPC9zdHJpbmc+CiAgICAgIDwvdm9pZD4KICAgICAgPHZvaWQgcHJvcGVydHk9ImNvZGUiPgogICAgICAgPHN0cmluZz4xMTIwMDc0MTA8L3N0cmluZz4KICAgICAgPC92b2lkPgogICAgICA8dm9pZCBwcm9wZXJ0eT0ibmFtZSI+CiAgICAgICA8c3RyaW5nPuWGjeioujwvc3RyaW5nPgogICAgICA8L3ZvaWQ+CiAgICAgPC9vYmplY3Q+CiAgICA8L3ZvaWQ+CiAgIDwvYXJyYXk+CiAgPC92b2lkPgogIDx2b2lkIHByb3BlcnR5PSJjbGFzc0NvZGUiPgogICA8c3RyaW5nPjEyMDwvc3RyaW5nPgogIDwvdm9pZD4KICA8dm9pZCBwcm9wZXJ0eT0iY2xhc3NDb2RlU3lzdGVtIj4KICAgPHN0cmluZz5DbGFpbTAwNzwvc3RyaW5nPgogIDwvdm9pZD4KICA8dm9pZCBwcm9wZXJ0eT0iY2xhc3NOYW1lIj4KICAgPHN0cmluZz7lho3oqLoo5YaN6Ki6KTwvc3RyaW5nPgogIDwvdm9pZD4KICA8dm9pZCBwcm9wZXJ0eT0ib3JkZXJOYW1lIj4KICAgPHN0cmluZz7oqLrmlq3mlpk8L3N0cmluZz4KICA8L3ZvaWQ+CiA8L29iamVjdD4KPC9qYXZhPgo=",
          "linkId": 0,
          "linkRelation": null,
          "recorded": 1526539476658,
          "userModel": {
            "userId": null,
            "facilityModel": null,
            "sirName": null,
            "givenName": null,
            "commonName": null,
            "departmentModel": null,
            "memo": null,
            "email": null,
            "orcaId": null,
            "useDrugId": null,
            "memberType": null,
            "registeredDate": null,
            "licenseModel": null,
            "roles": null,
            "password": null,
            "id": 13
          },
          "karteBean": {
            "lastDocDate": null,
            "created": null,
            "allergies": null,
            "heights": null,
            "weights": null,
            "patientVisits": null,
            "docInfoList": null,
            "memoList": null,
            "id": 33
          },
          "started": 1526539476658,
          "ended": null,
          "moduleInfoBean": {
            "stampNumber": 1,
            "stampName": "再診",
            "stampRole": "p",
            "entity": "baseChargeOrder"
          },
          "confirmed": 1526539476658,
          "status": "F",
          "id": 81
        },
        {
          "beanBytes": "PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPGphdmEgdmVyc2lvbj0iMS44LjBfMTYxIiBjbGFzcz0iamF2YS5iZWFucy5YTUxEZWNvZGVyIj4KIDxvYmplY3QgY2xhc3M9Im9wZW4uZG9scGhpbi5pbmZvbW9kZWwuQnVuZGxlTWVkIj4KICA8dm9pZCBwcm9wZXJ0eT0iYWRtaW4iPgogICA8c3RyaW5nPlvnlKjms5VdIO+8keaXpe+8k+Wbnuavjumjn+W+jOOBqzwvc3RyaW5nPgogIDwvdm9pZD4KICA8dm9pZCBwcm9wZXJ0eT0iYWRtaW5Db2RlIj4KICAgPHN0cmluZz4wMDEwMDAzMDE8L3N0cmluZz4KICA8L3ZvaWQ+CiAgPHZvaWQgcHJvcGVydHk9ImJ1bmRsZU51bWJlciI+CiAgIDxzdHJpbmc+Nzwvc3RyaW5nPgogIDwvdm9pZD4KICA8dm9pZCBwcm9wZXJ0eT0iY2xhaW1JdGVtIj4KICAgPGFycmF5IGNsYXNzPSJvcGVuLmRvbHBoaW4uaW5mb21vZGVsLkNsYWltSXRlbSIgbGVuZ3RoPSIxIj4KICAgIDx2b2lkIGluZGV4PSIwIj4KICAgICA8b2JqZWN0IGNsYXNzPSJvcGVuLmRvbHBoaW4uaW5mb21vZGVsLkNsYWltSXRlbSI+CiAgICAgIDx2b2lkIHByb3BlcnR5PSJjbGFzc0NvZGUiPgogICAgICAgPHN0cmluZz4yPC9zdHJpbmc+CiAgICAgIDwvdm9pZD4KICAgICAgPHZvaWQgcHJvcGVydHk9ImNsYXNzQ29kZVN5c3RlbSI+CiAgICAgICA8c3RyaW5nPkNsYWltMDAzPC9zdHJpbmc+CiAgICAgIDwvdm9pZD4KICAgICAgPHZvaWQgcHJvcGVydHk9ImNvZGUiPgogICAgICAgPHN0cmluZz42MTA0MjEzMjA8L3N0cmluZz4KICAgICAgPC92b2lkPgogICAgICA8dm9pZCBwcm9wZXJ0eT0ibmFtZSI+CiAgICAgICA8c3RyaW5nPuOCouODgOODqeODvOODiO+8o++8sumMoO+8ke+8kO+9je+9hzwvc3RyaW5nPgogICAgICA8L3ZvaWQ+CiAgICAgIDx2b2lkIHByb3BlcnR5PSJudW1iZXIiPgogICAgICAgPHN0cmluZz4zPC9zdHJpbmc+CiAgICAgIDwvdm9pZD4KICAgICAgPHZvaWQgcHJvcGVydHk9Im51bWJlckNvZGUiPgogICAgICAgPHN0cmluZz4xMDwvc3RyaW5nPgogICAgICA8L3ZvaWQ+CiAgICAgIDx2b2lkIHByb3BlcnR5PSJudW1iZXJDb2RlU3lzdGVtIj4KICAgICAgIDxzdHJpbmc+Q2xhaW0wMDQ8L3N0cmluZz4KICAgICAgPC92b2lkPgogICAgICA8dm9pZCBwcm9wZXJ0eT0idW5pdCI+CiAgICAgICA8c3RyaW5nPumMoDwvc3RyaW5nPgogICAgICA8L3ZvaWQ+CiAgICAgIDx2b2lkIHByb3BlcnR5PSJ5a3pLYm4iPgogICAgICAgPHN0cmluZz4xPC9zdHJpbmc+CiAgICAgIDwvdm9pZD4KICAgICA8L29iamVjdD4KICAgIDwvdm9pZD4KICAgPC9hcnJheT4KICA8L3ZvaWQ+CiAgPHZvaWQgcHJvcGVydHk9ImNsYXNzQ29kZSI+CiAgIDxzdHJpbmc+MjExPC9zdHJpbmc+CiAgPC92b2lkPgogIDx2b2lkIHByb3BlcnR5PSJjbGFzc0NvZGVTeXN0ZW0iPgogICA8c3RyaW5nPkNsYWltMDA3PC9zdHJpbmc+CiAgPC92b2lkPgogIDx2b2lkIHByb3BlcnR5PSJjbGFzc05hbWUiPgogICA8c3RyaW5nPuaKleiWrCjlhoXmnI3jg7vpoJPmnI3jg7voqr/liaQpKOmZouWGhSk8L3N0cmluZz4KICA8L3ZvaWQ+CiAgPHZvaWQgcHJvcGVydHk9Im1lbW8iPgogICA8c3RyaW5nPuWGheeUqO+8iOmZouWGheWHpuaWuSk8L3N0cmluZz4KICA8L3ZvaWQ+CiA8L29iamVjdD4KPC9qYXZhPgo=",
          "linkId": 0,
          "linkRelation": null,
          "recorded": 1526539476658,
          "userModel": {
            "userId": null,
            "facilityModel": null,
            "sirName": null,
            "givenName": null,
            "commonName": null,
            "departmentModel": null,
            "memo": null,
            "email": null,
            "orcaId": null,
            "useDrugId": null,
            "memberType": null,
            "registeredDate": null,
            "licenseModel": null,
            "roles": null,
            "password": null,
            "id": 13
          },
          "karteBean": {
            "lastDocDate": null,
            "created": null,
            "allergies": null,
            "heights": null,
            "weights": null,
            "patientVisits": null,
            "docInfoList": null,
            "memoList": null,
            "id": 33
          },
          "started": 1526539476658,
          "ended": null,
          "moduleInfoBean": {
            "stampNumber": 2,
            "stampName": "アダラートCR錠10mg",
            "stampRole": "p",
            "entity": "medOrder"
          },
          "confirmed": 1526539476658,
          "status": "F",
          "id": 82
        },
        {
          "beanBytes": "PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPGphdmEgdmVyc2lvbj0iMS44LjBfMTYxIiBjbGFzcz0iamF2YS5iZWFucy5YTUxEZWNvZGVyIj4KIDxvYmplY3QgY2xhc3M9Im9wZW4uZG9scGhpbi5pbmZvbW9kZWwuUHJvZ3Jlc3NDb3Vyc2UiPgogIDx2b2lkIHByb3BlcnR5PSJmcmVlVGV4dCI+CiAgIDxzdHJpbmc+Jmx0O3NlY3Rpb24gc3RhcnQ9JnF1b3Q7MCZxdW90OyBlbmQ9JnF1b3Q7NSZxdW90OyZndDsmbHQ7cGFyYWdyYXBoIHN0YXJ0PSZxdW90OzAmcXVvdDsgZW5kPSZxdW90OzImcXVvdDsmZ3Q7Jmx0O2NvbXBvbmVudCBzdGFydD0mcXVvdDswJnF1b3Q7IGVuZD0mcXVvdDsxJnF1b3Q7IG5hbWU9JnF1b3Q7c3RhbXBIb2xkZXImcXVvdDsgY29tcG9uZW50PSZxdW90OzAmcXVvdDsmZ3Q7Jmx0Oy9jb21wb25lbnQmZ3Q7Jmx0O2NvbnRlbnQgc3RhcnQ9JnF1b3Q7MSZxdW90OyBlbmQ9JnF1b3Q7MiZxdW90OyZndDsmbHQ7dGV4dCZndDsKJmx0Oy90ZXh0Jmd0OyZsdDsvY29udGVudCZndDsmbHQ7L3BhcmFncmFwaCZndDsmbHQ7cGFyYWdyYXBoIHN0YXJ0PSZxdW90OzImcXVvdDsgZW5kPSZxdW90OzMmcXVvdDsmZ3Q7Jmx0O2NvbnRlbnQgc3RhcnQ9JnF1b3Q7MiZxdW90OyBlbmQ9JnF1b3Q7MyZxdW90OyZndDsmbHQ7dGV4dCZndDsKJmx0Oy90ZXh0Jmd0OyZsdDsvY29udGVudCZndDsmbHQ7L3BhcmFncmFwaCZndDsmbHQ7cGFyYWdyYXBoIHN0YXJ0PSZxdW90OzMmcXVvdDsgZW5kPSZxdW90OzUmcXVvdDsmZ3Q7Jmx0O2NvbXBvbmVudCBzdGFydD0mcXVvdDszJnF1b3Q7IGVuZD0mcXVvdDs0JnF1b3Q7IG5hbWU9JnF1b3Q7c3RhbXBIb2xkZXImcXVvdDsgY29tcG9uZW50PSZxdW90OzEmcXVvdDsmZ3Q7Jmx0Oy9jb21wb25lbnQmZ3Q7Jmx0O2NvbnRlbnQgc3RhcnQ9JnF1b3Q7NCZxdW90OyBlbmQ9JnF1b3Q7NSZxdW90OyZndDsmbHQ7dGV4dCZndDsKJmx0Oy90ZXh0Jmd0OyZsdDsvY29udGVudCZndDsmbHQ7L3BhcmFncmFwaCZndDsmbHQ7L3NlY3Rpb24mZ3Q7PC9zdHJpbmc+CiAgPC92b2lkPgogPC9vYmplY3Q+CjwvamF2YT4K",
          "linkId": 0,
          "linkRelation": null,
          "recorded": 1526539476658,
          "userModel": {
            "userId": null,
            "facilityModel": null,
            "sirName": null,
            "givenName": null,
            "commonName": null,
            "departmentModel": null,
            "memo": null,
            "email": null,
            "orcaId": null,
            "useDrugId": null,
            "memberType": null,
            "registeredDate": null,
            "licenseModel": null,
            "roles": null,
            "password": null,
            "id": 13
          },
          "karteBean": {
            "lastDocDate": null,
            "created": null,
            "allergies": null,
            "heights": null,
            "weights": null,
            "patientVisits": null,
            "docInfoList": null,
            "memoList": null,
            "id": 33
          },
          "started": 1526539476658,
          "ended": null,
          "moduleInfoBean": {
            "stampNumber": 3,
            "stampName": "progressCourse",
            "stampRole": "pSpec",
            "entity": "progressCourse"
          },
          "confirmed": 1526539476658,
          "status": "F",
          "id": 83
        }
      ],
      "attachment": null,
      "schema": null,
      "status": "F",
      "id": 79
    }
  ]
}




#4 GET /dolphin/openSource/pvt2/pvtList HTTP/1.1
GET /dolphin/openSource/pvt2/pvtList HTTP/1.1
Accept: application/json
Accept-Encoding: gzip, deflate
password: 6f8e646f95af8e79096477e877245664
userName: 1.3.6.1.4.1.9414.70.1:w3415038
Host: 172.16.108.251:8080
Connection: Keep-Alive

HTTP/1.1 200 OK
Connection: keep-alive
X-Powered-By: Undertow/1
Server: WildFly/9
Content-Type: application/json
Content-Length: 2389
Date: Wed, 06 Jun 2018 04:01:51 GMT

{
  "list": [
    {
      "memo": null,
      "facilityId": "1.3.6.1.4.1.9414.70.1",
      "patientModel": {
        "romanName": null,
        "nationality": null,
        "nationalityDesc": null,
        "maritalStatus": null,
        "jpegPhoto": null,
        "mobilePhone": null,
        "relations": null,
        "reserve1": null,
        "reserve2": null,
        "reserve3": null,
        "reserve4": null,
        "reserve5": null,
        "reserve6": null,
        "memo": null,
        "email": null,
        "facilityId": "1.3.6.1.4.1.9414.70.1",
        "telephone": null,
        "patientId": "00004",
        "healthInsurances": [
          {
            "beanBytes": "PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPGphdmEgdmVyc2lvbj0iMS44LjBfMTYxIiBjbGFzcz0iamF2YS5iZWFucy5YTUxEZWNvZGVyIj4KIDxvYmplY3QgY2xhc3M9Im9wZW4uZG9scGhpbi5pbmZvbW9kZWwuUFZUSGVhbHRoSW5zdXJhbmNlTW9kZWwiPgogIDx2b2lkIHByb3BlcnR5PSJHVUlEIj4KICAgPHN0cmluZz41Yjk0NWIyZi01Mzg4LTQ0ODMtYjlhYS0wOTdkMDJhZDk1MmE8L3N0cmluZz4KICA8L3ZvaWQ+CiAgPHZvaWQgcHJvcGVydHk9ImNsaWVudEdyb3VwIj4KICAgPHN0cmluZz7vvJLvvJDvvJDvvJA8L3N0cmluZz4KICA8L3ZvaWQ+CiAgPHZvaWQgcHJvcGVydHk9ImNsaWVudE51bWJlciI+CiAgIDxzdHJpbmc+77yW77yY77yV77yTPC9zdHJpbmc+CiAgPC92b2lkPgogIDx2b2lkIHByb3BlcnR5PSJleHBpcmVkRGF0ZSI+CiAgIDxzdHJpbmc+OTk5OS0xMi0zMTwvc3RyaW5nPgogIDwvdm9pZD4KICA8dm9pZCBwcm9wZXJ0eT0iZmFtaWx5Q2xhc3MiPgogICA8c3RyaW5nPmZhbHNlPC9zdHJpbmc+CiAgPC92b2lkPgogIDx2b2lkIHByb3BlcnR5PSJpbnN1cmFuY2VDbGFzcyI+CiAgIDxzdHJpbmc+57WE5ZCIPC9zdHJpbmc+CiAgPC92b2lkPgogIDx2b2lkIHByb3BlcnR5PSJpbnN1cmFuY2VDbGFzc0NvZGUiPgogICA8c3RyaW5nPjA2PC9zdHJpbmc+CiAgPC92b2lkPgogIDx2b2lkIHByb3BlcnR5PSJpbnN1cmFuY2VDbGFzc0NvZGVTeXMiPgogICA8c3RyaW5nPk1NTDAwMzE8L3N0cmluZz4KICA8L3ZvaWQ+CiAgPHZvaWQgcHJvcGVydHk9Imluc3VyYW5jZU51bWJlciI+CiAgIDxzdHJpbmc+MDYzMzAwNzA8L3N0cmluZz4KICA8L3ZvaWQ+CiAgPHZvaWQgcHJvcGVydHk9InBheU91dFJhdGlvIj4KICAgPHN0cmluZz4wLjMwPC9zdHJpbmc+CiAgPC92b2lkPgogIDx2b2lkIHByb3BlcnR5PSJzdGFydERhdGUiPgogICA8c3RyaW5nPjIwMTctMTAtMzA8L3N0cmluZz4KICA8L3ZvaWQ+CiA8L29iamVjdD4KPC9qYXZhPgo=",
            "id": 32
          }
        ],
        "kanaName": "サヤマ セイジ",
        "gender": "male",
        "genderDesc": "M",
        "birthday": "1948-06-15",
        "simpleAddressModel": null,
        "appMemo": "診察1",
        "pvtDate": null,
        "ownerUUID": "w3415038:eacac34c-c8e7-4bd8-9e71-35f3c3653ddc",
        "fullName": "佐山 清二",
        "id": 31
      },
      "deptName": "内科",
      "deptCode": "01",
      "doctorName": "〇〇 〇〇",
      "doctorId": "10002",
      "jmariNumber": "JPN000000000000",
      "pvtDate": "2018-05-17T15:22:21",
      "department": "内科,01,〇〇 〇〇,10002,JPN000000000000,",
      "firstInsurance": "06 組合",
      "insuranceUid": "5b945b2f-5388-4483-b9aa-097d02ad952a",
      "lastDocDate": null,
      "appointment": null,
      "id": 34,
      "state": 1
    }
  ]
}


#3と#4のキャプチャに関しては、"beanBytes"の部分をBase64でコードツールでデコードをした。整形したのは下記の点である。

#5 <?xml version="1.0" encoding="UTF-8"?> (#3のキャプチャより)
<?xml version="1.0" encoding="UTF-8"?>
<java version="1.8.0_161" class="java.beans.XMLDecoder">
  <object class="open.dolphin.infomodel.ProgressCourse">
    <void property="freeText">
      <string>
        <section start="0" end="14">
          <paragraph start="0" end="14">
            <content start="0" end="13" name="content">
              <text>嘔吐・下痢の症状がみられる</text>
            </content>
            <content start="13" end="14">
              <text></text>
            </content>
          </paragraph>
        </section>
      </string>
    </void>
  </object>
</java>

#6 <?xml version="1.0" encoding="UTF-8"?> (#3のキャプチャより)
<?xml version="1.0" encoding="UTF-8"?>
<java version="1.8.0_161" class="java.beans.XMLDecoder">
  <object class="open.dolphin.infomodel.BundleDolphin">
    <void property="bundleNumber">
      <string>1</string>
    </void>
    <void property="claimItem">
      <array class="open.dolphin.infomodel.ClaimItem" length="1">
        <void index="0">
          <object class="open.dolphin.infomodel.ClaimItem">
            <void property="classCode">
              <string>0</string>
            </void>
            <void property="classCodeSystem">
              <string>Claim003</string>
            </void>
            <void property="code">
              <string>112007410</string>
            </void>
            <void property="name">
              <string>再診</string>
            </void>
          </object>
        </void>
      </array>
    </void>
    <void property="classCode">
      <string>120</string>
    </void>
    <void property="classCodeSystem">
      <string>Claim007</string>
    </void>
    <void property="className">
      <string>再診(再診)</string>
    </void>
    <void property="orderName">
      <string>診断料</string>
    </void>
  </object>
</java>
#7 <?xml version="1.0" encoding="UTF-8"?> (#3のキャプチャより)
<?xml version="1.0" encoding="UTF-8"?>
<java version="1.8.0_161" class="java.beans.XMLDecoder">
  <object class="open.dolphin.infomodel.BundleMed">
    <void property="admin">
      <string>[用法] 1日3回毎食後に</string>
    </void>
    <void property="adminCode">
      <string>001000301</string>
    </void>
    <void property="bundleNumber">
      <string>7</string>
    </void>
    <void property="claimItem">
      <array class="open.dolphin.infomodel.ClaimItem" length="1">
        <void index="0">
          <object class="open.dolphin.infomodel.ClaimItem">
            <void property="classCode">
              <string>2</string>
            </void>
            <void property="classCodeSystem">
              <string>Claim003</string>
            </void>
            <void property="code">
              <string>610421320</string>
            </void>
            <void property="name">
              <string>アダラートCR錠10mg</string>
            </void>
            <void property="number">
              <string>3</string>
            </void>
            <void property="numberCode">
              <string>10</string>
            </void>
            <void property="numberCodeSystem">
              <string>Claim004</string>
            </void>
            <void property="unit">
              <string>錠</string>
            </void>
            <void property="ykzKbn">
              <string>1</string>
            </void>
          </object>
        </void>
      </array>
    </void>
    <void property="classCode">
      <string>211</string>
    </void>
    <void property="classCodeSystem">
      <string>Claim007</string>
    </void>
    <void property="className">
      <string>投薬(内服・頓服・調剤)(院内)</string>
    </void>
    <void property="memo">
      <string>内用(院内処方)</string>
    </void>
  </object>
</java>


#8 <?xml version="1.0" encoding="UTF-8"?> (#4のキャプチャより)
<?xml version="1.0" encoding="UTF-8"?>
<java version="1.8.0_161" class="java.beans.XMLDecoder">
  <object class="open.dolphin.infomodel.ProgressCourse">
    <void property="freeText">
      <string>
        <section start="0" end="5">
          <paragraph start="0" end="2">
            <component start="0" end="1" name="stampHolder" component="0"></component>
            <content start="1" end="2">
              <text></text>
            </content>
          </paragraph>
          <paragraph start="2" end="3">
            <content start="2" end="3">
              <text></text>
            </content>
          </paragraph>
          <paragraph start="3" end="5">
            <component start="3" end="4" name="stampHolder" component="1"></component>
            <content start="4" end="5">
              <text></text>
            </content>
          </paragraph>
        </section>
      </string>
    </void>
  </object>
</java>


結論として、、、
#1カルテ画面の患者情報をやり取りしていることが分かる
#2HTTP PUTを用いてステート等の更新を促しているのだろうか?
#3HTTP GETからカルテの情報を読み込んでいる。
#4患者のIDや名前等の情報が読み取れた。
#5フリーテキストの開始では無いかと考える
#6再診や診察料の記載をしているのではないだろうか?
#7薬剤や用法について記載している
#8では、#5と同様にテキストの開始を表しているのではないだろうか?


3.患者検索

今回は前回と同様の手順を用いてOpenDolphinのパケットキャプチャを行い、実際にどのようなデータが流れているのかを確認した。尚、今回確認したパケットはキーワード「0000」で患者検索をし、検査結果が表示された際に送受信されるパケットである(図1,2)
図1.キーワード「0000」で検索する際の画面
図2.検査結果画面
図3.Wiresharkで患者検索時に流れていたパケットをキャプチャ
取得したパケットは上記の図3の様なものである。ここからprtocolが「HTTP」、Infoが「GET~」から始まるパケットを探すと全部で2つあった。各々に対して、[追跡]>[HTTPストリーム]でパケットの再構築を行った。するとポップアップが表示されるので、show and save data asの項目を「UTF-8形式」に変更し、save as...よりパケットの保存を行う。尚、この際拡張子は「.txt」とする。


その、2つのパケットの結果が以下の2つである(以下はJSON Pretty Printを用い、すでにデータを整形し置き換えている)。



#1 GET /dolphin/openSource/patient/digit/0000 HTTP/1.1

GET /dolphin/openSource/patient/digit/0000 HTTP/1.1
Accept: application/json
Accept-Encoding: gzip, deflate
password: 6f8e646f95af8e79096477e877245664
userName: 1.3.6.1.4.1.9414.70.1:w3415016
Host: 172.16.108.251:8080
Connection: Keep-Alive

HTTP/1.1 200 OK
Connection: keep-alive
X-Powered-By: Undertow/1
Server: WildFly/9
Content-Type: application/json
Content-Length: 7787
Date: Wed, 06 Jun 2018 03:54:57 GMT

{
  "list": [
    {
      "romanName": null,
      "nationality": null,
      "nationalityDesc": null,
      "maritalStatus": null,
      "jpegPhoto": null,
      "mobilePhone": null,
      "relations": null,
      "reserve1": null,
      "reserve2": null,
      "reserve3": null,
      "reserve4": null,
      "reserve5": null,
      "reserve6": null,
      "memo": null,
      "email": null,
      "facilityId": "1.3.6.1.4.1.9414.70.1",
      "telephone": null,
      "patientId": "00004",
      "healthInsurances": [
        {
          "beanBytes": "PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPGphdmEgdmVyc2lvbj0iMS44LjBfMTYxIiBjbGFzcz0iamF2YS5iZWFucy5YTUxEZWNvZGVyIj4KIDxvYmplY3QgY2xhc3M9Im9wZW4uZG9scGhpbi5pbmZvbW9kZWwuUFZUSGVhbHRoSW5zdXJhbmNlTW9kZWwiPgogIDx2b2lkIHByb3BlcnR5PSJHVUlEIj4KICAgPHN0cmluZz41Yjk0NWIyZi01Mzg4LTQ0ODMtYjlhYS0wOTdkMDJhZDk1MmE8L3N0cmluZz4KICA8L3ZvaWQ+CiAgPHZvaWQgcHJvcGVydHk9ImNsaWVudEdyb3VwIj4KICAgPHN0cmluZz7vvJLvvJDvvJDvvJA8L3N0cmluZz4KICA8L3ZvaWQ+CiAgPHZvaWQgcHJvcGVydHk9ImNsaWVudE51bWJlciI+CiAgIDxzdHJpbmc+77yW77yY77yV77yTPC9zdHJpbmc+CiAgPC92b2lkPgogIDx2b2lkIHByb3BlcnR5PSJleHBpcmVkRGF0ZSI+CiAgIDxzdHJpbmc+OTk5OS0xMi0zMTwvc3RyaW5nPgogIDwvdm9pZD4KICA8dm9pZCBwcm9wZXJ0eT0iZmFtaWx5Q2xhc3MiPgogICA8c3RyaW5nPmZhbHNlPC9zdHJpbmc+CiAgPC92b2lkPgogIDx2b2lkIHByb3BlcnR5PSJpbnN1cmFuY2VDbGFzcyI+CiAgIDxzdHJpbmc+57WE5ZCIPC9zdHJpbmc+CiAgPC92b2lkPgogIDx2b2lkIHByb3BlcnR5PSJpbnN1cmFuY2VDbGFzc0NvZGUiPgogICA8c3RyaW5nPjA2PC9zdHJpbmc+CiAgPC92b2lkPgogIDx2b2lkIHByb3BlcnR5PSJpbnN1cmFuY2VDbGFzc0NvZGVTeXMiPgogICA8c3RyaW5nPk1NTDAwMzE8L3N0cmluZz4KICA8L3ZvaWQ+CiAgPHZvaWQgcHJvcGVydHk9Imluc3VyYW5jZU51bWJlciI+CiAgIDxzdHJpbmc+MDYzMzAwNzA8L3N0cmluZz4KICA8L3ZvaWQ+CiAgPHZvaWQgcHJvcGVydHk9InBheU91dFJhdGlvIj4KICAgPHN0cmluZz4wLjMwPC9zdHJpbmc+CiAgPC92b2lkPgogIDx2b2lkIHByb3BlcnR5PSJzdGFydERhdGUiPgogICA8c3RyaW5nPjIwMTctMTAtMzA8L3N0cmluZz4KICA8L3ZvaWQ+CiA8L29iamVjdD4KPC9qYXZhPgo=",
          "id": 32
        }
      ],
      "kanaName": "サヤマ セイジ",
      "gender": "male",
      "genderDesc": "M",
      "birthday": "1948-06-15",
      "simpleAddressModel": null,
      "appMemo": "診察1",
      "pvtDate": "2018-05-17T15:22:21",
      "ownerUUID": null,
      "fullName": "佐山 清二",
      "id": 31
    },
    {
      "romanName": null,
      "nationality": null,
      "nationalityDesc": null,
      "maritalStatus": null,
      "jpegPhoto": null,
      "mobilePhone": null,
      "relations": null,
      "reserve1": null,
      "reserve2": null,
      "reserve3": null,
      "reserve4": null,
      "reserve5": null,
      "reserve6": null,
      "memo": null,
      "email": null,
      "facilityId": "1.3.6.1.4.1.9414.70.1",
      "telephone": null,
      "patientId": "00001",
      "healthInsurances": [
        {
          "beanBytes": "PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPGphdmEgdmVyc2lvbj0iMS44LjBfMTcxIiBjbGFzcz0iamF2YS5iZWFucy5YTUxEZWNvZGVyIj4KIDxvYmplY3QgY2xhc3M9Im9wZW4uZG9scGhpbi5pbmZvbW9kZWwuUFZUSGVhbHRoSW5zdXJhbmNlTW9kZWwiPgogIDx2b2lkIHByb3BlcnR5PSJHVUlEIj4KICAgPHN0cmluZz45MjYyOTRlZi03YTZmLTRlOTYtODEyNS03OThlMzQwYWNhNjM8L3N0cmluZz4KICA8L3ZvaWQ+CiAgPHZvaWQgcHJvcGVydHk9ImNsaWVudEdyb3VwIj4KICAgPHN0cmluZz7vvJTvvJHvvJDvvJbvvJHvvJLvvJTvvJI8L3N0cmluZz4KICA8L3ZvaWQ+CiAgPHZvaWQgcHJvcGVydHk9ImNsaWVudE51bWJlciI+CiAgIDxzdHJpbmc+77yZ77yW77yT77yVPC9zdHJpbmc+CiAgPC92b2lkPgogIDx2b2lkIHByb3BlcnR5PSJleHBpcmVkRGF0ZSI+CiAgIDxzdHJpbmc+OTk5OS0xMi0zMTwvc3RyaW5nPgogIDwvdm9pZD4KICA8dm9pZCBwcm9wZXJ0eT0iZmFtaWx5Q2xhc3MiPgogICA8c3RyaW5nPnRydWU8L3N0cmluZz4KICA8L3ZvaWQ+CiAgPHZvaWQgcHJvcGVydHk9Imluc3VyYW5jZUNsYXNzIj4KICAgPHN0cmluZz7ljZTkvJrjgZHjgpPjgb08L3N0cmluZz4KICA8L3ZvaWQ+CiAgPHZvaWQgcHJvcGVydHk9Imluc3VyYW5jZUNsYXNzQ29kZSI+CiAgIDxzdHJpbmc+MDk8L3N0cmluZz4KICA8L3ZvaWQ+CiAgPHZvaWQgcHJvcGVydHk9Imluc3VyYW5jZUNsYXNzQ29kZVN5cyI+CiAgIDxzdHJpbmc+TU1MMDAzMTwvc3RyaW5nPgogIDwvdm9pZD4KICA8dm9pZCBwcm9wZXJ0eT0iaW5zdXJhbmNlTnVtYmVyIj4KICAgPHN0cmluZz4wMTQwMDAxOTwvc3RyaW5nPgogIDwvdm9pZD4KICA8dm9pZCBwcm9wZXJ0eT0icGF5T3V0UmF0aW8iPgogICA8c3RyaW5nPjAuMzA8L3N0cmluZz4KICA8L3ZvaWQ+CiAgPHZvaWQgcHJvcGVydHk9InN0YXJ0RGF0ZSI+CiAgIDxzdHJpbmc+MjAxNy0xMC0yNTwvc3RyaW5nPgogIDwvdm9pZD4KIDwvb2JqZWN0Pgo8L2phdmE+Cg==",
          "id": 1867
        }
      ],
      "kanaName": "ヤマシタ コウスケ",
      "gender": "male",
      "genderDesc": "M",
      "birthday": "1970-12-06",
      "simpleAddressModel": null,
      "appMemo": "診察1",
      "pvtDate": "2018-05-23T13:56:45",
      "ownerUUID": null,
      "fullName": "山下 浩介",
      "id": 35
    },
    {
      "romanName": null,
      "nationality": null,
      "nationalityDesc": null,
      "maritalStatus": null,
      "jpegPhoto": null,
      "mobilePhone": null,
      "relations": null,
      "reserve1": null,
      "reserve2": null,
      "reserve3": null,
      "reserve4": null,
      "reserve5": null,
      "reserve6": null,
      "memo": null,
      "email": null,
      "facilityId": "1.3.6.1.4.1.9414.70.1",
      "telephone": null,
      "patientId": "00002",
      "healthInsurances": [
        {
          "beanBytes": "PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPGphdmEgdmVyc2lvbj0iMS44LjBfNDUiIGNsYXNzPSJqYXZhLmJlYW5zLlhNTERlY29kZXIiPgogPG9iamVjdCBjbGFzcz0ib3Blbi5kb2xwaGluLmluZm9tb2RlbC5QVlRIZWFsdGhJbnN1cmFuY2VNb2RlbCI+CiAgPHZvaWQgcHJvcGVydHk9IkdVSUQiPgogICA8c3RyaW5nPjFhYWM3MjMyLWEwZWEtNDM2Ny1hODMwLTE0OTE5NjMzYTYzZjwvc3RyaW5nPgogIDwvdm9pZD4KICA8dm9pZCBwcm9wZXJ0eT0iY2xpZW50R3JvdXAiPgogICA8c3RyaW5nPumDvTwvc3RyaW5nPgogIDwvdm9pZD4KICA8dm9pZCBwcm9wZXJ0eT0iY2xpZW50TnVtYmVyIj4KICAgPHN0cmluZz7vvJfvvJfvvJTvvJLvvJE8L3N0cmluZz4KICA8L3ZvaWQ+CiAgPHZvaWQgcHJvcGVydHk9ImV4cGlyZWREYXRlIj4KICAgPHN0cmluZz45OTk5LTEyLTMxPC9zdHJpbmc+CiAgPC92b2lkPgogIDx2b2lkIHByb3BlcnR5PSJmYW1pbHlDbGFzcyI+CiAgIDxzdHJpbmc+dHJ1ZTwvc3RyaW5nPgogIDwvdm9pZD4KICA8dm9pZCBwcm9wZXJ0eT0iaW5zdXJhbmNlQ2xhc3MiPgogICA8c3RyaW5nPuWcsOaWueWFsea4iDwvc3RyaW5nPgogIDwvdm9pZD4KICA8dm9pZCBwcm9wZXJ0eT0iaW5zdXJhbmNlQ2xhc3NDb2RlIj4KICAgPHN0cmluZz4zMjwvc3RyaW5nPgogIDwvdm9pZD4KICA8dm9pZCBwcm9wZXJ0eT0iaW5zdXJhbmNlQ2xhc3NDb2RlU3lzIj4KICAgPHN0cmluZz5NTUwwMDMxPC9zdHJpbmc+CiAgPC92b2lkPgogIDx2b2lkIHByb3BlcnR5PSJpbnN1cmFuY2VOdW1iZXIiPgogICA8c3RyaW5nPjMyMTMwMjEzPC9zdHJpbmc+CiAgPC92b2lkPgogIDx2b2lkIHByb3BlcnR5PSJwYXlPdXRSYXRpbyI+CiAgIDxzdHJpbmc+MC4zMDwvc3RyaW5nPgogIDwvdm9pZD4KICA8dm9pZCBwcm9wZXJ0eT0ic3RhcnREYXRlIj4KICAgPHN0cmluZz4yMDE3LTEwLTI1PC9zdHJpbmc+CiAgPC92b2lkPgogPC9vYmplY3Q+CjwvamF2YT4K",
          "id": 40
        }
      ],
      "kanaName": "フクダ ヨシヒロ",
      "gender": "male",
      "genderDesc": "M",
      "birthday": "1961-08-27",
      "simpleAddressModel": null,
      "appMemo": "診察1",
      "pvtDate": "2018-05-17T15:22:52",
      "ownerUUID": null,
      "fullName": "福田 佳宏",
      "id": 39
    },
    {
      "romanName": null,
      "nationality": null,
      "nationalityDesc": null,
      "maritalStatus": null,
      "jpegPhoto": null,
      "mobilePhone": null,
      "relations": null,
      "reserve1": null,
      "reserve2": null,
      "reserve3": null,
      "reserve4": null,
      "reserve5": null,
      "reserve6": null,
      "memo": null,
      "email": null,
      "facilityId": "1.3.6.1.4.1.9414.70.1",
      "telephone": null,
      "patientId": "00003",
      "healthInsurances": [
        {
          "beanBytes": "PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPGphdmEgdmVyc2lvbj0iMS44LjBfMTcxIiBjbGFzcz0iamF2YS5iZWFucy5YTUxEZWNvZGVyIj4KIDxvYmplY3QgY2xhc3M9Im9wZW4uZG9scGhpbi5pbmZvbW9kZWwuUFZUSGVhbHRoSW5zdXJhbmNlTW9kZWwiPgogIDx2b2lkIHByb3BlcnR5PSJHVUlEIj4KICAgPHN0cmluZz40MDIyNjE1ZC1kNWE0LTRiY2ItYWJiMC1hZTY2MTEzY2FjMTg8L3N0cmluZz4KICA8L3ZvaWQ+CiAgPHZvaWQgcHJvcGVydHk9ImNsaWVudEdyb3VwIj4KICAgPHN0cmluZz7vvJHvvJDvvJg8L3N0cmluZz4KICA8L3ZvaWQ+CiAgPHZvaWQgcHJvcGVydHk9ImNsaWVudE51bWJlciI+CiAgIDxzdHJpbmc+77yU77yT77yS77yV77yS77yRPC9zdHJpbmc+CiAgPC92b2lkPgogIDx2b2lkIHByb3BlcnR5PSJleHBpcmVkRGF0ZSI+CiAgIDxzdHJpbmc+OTk5OS0xMi0zMTwvc3RyaW5nPgogIDwvdm9pZD4KICA8dm9pZCBwcm9wZXJ0eT0iZmFtaWx5Q2xhc3MiPgogICA8c3RyaW5nPmZhbHNlPC9zdHJpbmc+CiAgPC92b2lkPgogIDx2b2lkIHByb3BlcnR5PSJpbnN1cmFuY2VDbGFzcyI+CiAgIDxzdHJpbmc+57WE5ZCIPC9zdHJpbmc+CiAgPC92b2lkPgogIDx2b2lkIHByb3BlcnR5PSJpbnN1cmFuY2VDbGFzc0NvZGUiPgogICA8c3RyaW5nPjA2PC9zdHJpbmc+CiAgPC92b2lkPgogIDx2b2lkIHByb3BlcnR5PSJpbnN1cmFuY2VDbGFzc0NvZGVTeXMiPgogICA8c3RyaW5nPk1NTDAwMzE8L3N0cmluZz4KICA8L3ZvaWQ+CiAgPHZvaWQgcHJvcGVydHk9Imluc3VyYW5jZU51bWJlciI+CiAgIDxzdHJpbmc+MDYwNDAxMzM8L3N0cmluZz4KICA8L3ZvaWQ+CiAgPHZvaWQgcHJvcGVydHk9InBheU91dFJhdGlvIj4KICAgPHN0cmluZz4wLjMwPC9zdHJpbmc+CiAgPC92b2lkPgogIDx2b2lkIHByb3BlcnR5PSJzdGFydERhdGUiPgogICA8c3RyaW5nPjIwMTctMTAtMzA8L3N0cmluZz4KICA8L3ZvaWQ+CiA8L29iamVjdD4KPC9qYXZhPgo=",
          "id": 28
        }
      ],
      "kanaName": "サカイ ハルヨ",
      "gender": "female",
      "genderDesc": "F",
      "birthday": "1976-06-04",
      "simpleAddressModel": null,
      "appMemo": "診察1",
      "pvtDate": "2018-05-17T15:22:20",
      "ownerUUID": null,
      "fullName": "酒井 春代",
      "id": 27
    }
  ]
}








#2 GET /dolphin/openSource/pvt2/pvtList HTTP/1.1


GET /dolphin/openSource/pvt2/pvtList HTTP/1.1
Accept: application/json
Accept-Encoding: gzip, deflate
password: 6f8e646f95af8e79096477e877245664
userName: 1.3.6.1.4.1.9414.70.1:w3415016
Host: 172.16.108.251:8080
Connection: Keep-Alive

HTTP/1.1 200 OK
Connection: keep-alive
X-Powered-By: Undertow/1
Server: WildFly/9
Content-Type: application/json
Content-Length: 2346
Date: Wed, 06 Jun 2018 03:55:08 GMT

{
  "list": [
    {
      "memo": null,
      "facilityId": "1.3.6.1.4.1.9414.70.1",
      "patientModel": {
        "romanName": null,
        "nationality": null,
        "nationalityDesc": null,
        "maritalStatus": null,
        "jpegPhoto": null,
        "mobilePhone": null,
        "relations": null,
        "reserve1": null,
        "reserve2": null,
        "reserve3": null,
        "reserve4": null,
        "reserve5": null,
        "reserve6": null,
        "memo": null,
        "email": null,
        "facilityId": "1.3.6.1.4.1.9414.70.1",
        "telephone": null,
        "patientId": "00004",
        "healthInsurances": [
          {
            "beanBytes": "PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPGphdmEgdmVyc2lvbj0iMS44LjBfMTYxIiBjbGFzcz0iamF2YS5iZWFucy5YTUxEZWNvZGVyIj4KIDxvYmplY3QgY2xhc3M9Im9wZW4uZG9scGhpbi5pbmZvbW9kZWwuUFZUSGVhbHRoSW5zdXJhbmNlTW9kZWwiPgogIDx2b2lkIHByb3BlcnR5PSJHVUlEIj4KICAgPHN0cmluZz41Yjk0NWIyZi01Mzg4LTQ0ODMtYjlhYS0wOTdkMDJhZDk1MmE8L3N0cmluZz4KICA8L3ZvaWQ+CiAgPHZvaWQgcHJvcGVydHk9ImNsaWVudEdyb3VwIj4KICAgPHN0cmluZz7vvJLvvJDvvJDvvJA8L3N0cmluZz4KICA8L3ZvaWQ+CiAgPHZvaWQgcHJvcGVydHk9ImNsaWVudE51bWJlciI+CiAgIDxzdHJpbmc+77yW77yY77yV77yTPC9zdHJpbmc+CiAgPC92b2lkPgogIDx2b2lkIHByb3BlcnR5PSJleHBpcmVkRGF0ZSI+CiAgIDxzdHJpbmc+OTk5OS0xMi0zMTwvc3RyaW5nPgogIDwvdm9pZD4KICA8dm9pZCBwcm9wZXJ0eT0iZmFtaWx5Q2xhc3MiPgogICA8c3RyaW5nPmZhbHNlPC9zdHJpbmc+CiAgPC92b2lkPgogIDx2b2lkIHByb3BlcnR5PSJpbnN1cmFuY2VDbGFzcyI+CiAgIDxzdHJpbmc+57WE5ZCIPC9zdHJpbmc+CiAgPC92b2lkPgogIDx2b2lkIHByb3BlcnR5PSJpbnN1cmFuY2VDbGFzc0NvZGUiPgogICA8c3RyaW5nPjA2PC9zdHJpbmc+CiAgPC92b2lkPgogIDx2b2lkIHByb3BlcnR5PSJpbnN1cmFuY2VDbGFzc0NvZGVTeXMiPgogICA8c3RyaW5nPk1NTDAwMzE8L3N0cmluZz4KICA8L3ZvaWQ+CiAgPHZvaWQgcHJvcGVydHk9Imluc3VyYW5jZU51bWJlciI+CiAgIDxzdHJpbmc+MDYzMzAwNzA8L3N0cmluZz4KICA8L3ZvaWQ+CiAgPHZvaWQgcHJvcGVydHk9InBheU91dFJhdGlvIj4KICAgPHN0cmluZz4wLjMwPC9zdHJpbmc+CiAgPC92b2lkPgogIDx2b2lkIHByb3BlcnR5PSJzdGFydERhdGUiPgogICA8c3RyaW5nPjIwMTctMTAtMzA8L3N0cmluZz4KICA8L3ZvaWQ+CiA8L29iamVjdD4KPC9qYXZhPgo=",
            "id": 32
          }
        ],
        "kanaName": "サヤマ セイジ",
        "gender": "male",
        "genderDesc": "M",
        "birthday": "1948-06-15",
        "simpleAddressModel": null,
        "appMemo": "診察1",
        "pvtDate": null,
        "ownerUUID": null,
        "fullName": "佐山 清二",
        "id": 31
      },
      "deptName": "内科",
      "deptCode": "01",
      "doctorName": "〇〇 ○○",
      "doctorId": "10002",
      "jmariNumber": "JPN000000000000",
      "pvtDate": "2018-05-17T15:22:21",
      "department": "内科,01,○○ ○○,10002,JPN000000000000,",
      "firstInsurance": "06 組合",
      "insuranceUid": "5b945b2f-5388-4483-b9aa-097d02ad952a",
      "lastDocDate": null,
      "appointment": null,
      "id": 34,
      "state": 0
    }
  ]
}

#1は患者の情報を1人ずつ項目ごとにデータを要求していることが分かった。
#2は前回のブログにあったよう、保険情報のデータのやり取りを行っていることが分かった。

レーダーチャートの表示2

前回 レーダーチャートの表示を行うことが出来たので、今回は実際の値を代入したグラフの描画を試みる。 .controller('RaderChartController', ['$scope', 'Countries', funct...