Main Content

Figure ウィンドウでのストリームデータの表示

この例では、HTTP StringConsumer クラス PricesStreamer をカスタマイズして、架空の Web サイトからのストリームデータを MATLAB® Figure ウィンドウに表示する方法を説明します。実際に動作する例を作成するには、次を行います。

  • 次のような URL を識別する

    url = matlab.net.URI('<URL>','accountId',<YOUR_ACCOUNT_ID>,'<NAME>','<VALUE>');
  • PricesStreamer.putData を変更して、Web サービス固有のデータを読み取る

このトピックでは、次のタスクについて説明しています。Figure ウィンドウで table データを表示する方法の詳細については、uitable を参照してください。

PricesStreamer クラス

PricesStreamer.mStringConsumer クラスのサブクラスで、StringConsumer は ContentConsumer のサブクラスです。PricesStreamer は、特定の Web サービスによって提供されるデータ用にカスタマイズされたストリームデータを受信します。この例では、データの構造は次のとおりです。

% Data contains one or more CRLF-separated JSON structures.  
% Each structure is one of the format:
%   {"heartbeat"="timestamp"}
%   {"tick"="timestamp", "bid"=bid, "ask"=ask}
% where timestamp is a GMT time and bid and ask are numbers.

MATLAB は、サーバーから受信する各データ チャンクに対して関数 PricesStreamer.putData を呼び出します。この関数は、まず StringConsumer を使用して生の uint8 バイトを JSON 文字列に変換します。次に、jsondecode を使用して JSON 文字列から MATLAB 構造体を取得し、そのデータを Figure 内の table として表示します。その際、データをインクリメントするごとに table の上部に 1 行追加されます。関数 putData に変更を加えて、データに他の操作を行うこともできます。たとえば、リアルタイム グラフをプロットする、価格の差を表示するなどです。PricesStreamer は戻り値 stop を設定して、ユーザーが Figure を閉じたときに操作が停止するようにします。詳細については、putData を参照してください。

classdef PricesStreamer < matlab.net.http.io.StringConsumer
    % PricesStreamer accepts streamed JSON 
    % and displays the result in a uitable in a figure window.
    
    % Copyright 2016-2017 The MathWorks, Inc.
    properties
        Figure
        Table
        Endit logical
        HaveTick logical
    end
    
    methods (Access=protected)
        function length = start(obj)
            if obj.Response.StatusCode ~= matlab.net.http.StatusCode.OK
                length = 0;
            else
                length = obj.start@matlab.net.http.io.StringConsumer;
                obj.Figure = figure('CloseRequestFcn',@obj.endit);
                obj.Figure.Position(4) = 550;
                obj.Figure.Position(2) = 50;
                obj.Table = uitable(obj.Figure,...
                    'ColumnName',{'Time','Bid','Ask'},...
                    'ColumnWidth',{130,'auto','auto'});
                obj.Table.Position(4) = 500;
                obj.Table.Data = cell(0,3);
                obj.Endit = false;
                obj.HaveTick = false;
            end
        end
    end
    
    methods
        function [len,stop] = putData(obj, data)
            % Data contains one or more CRLF-separated JSON structures.  
            % Each structure is one of the format:
            %   {"heartbeat"="timestamp"}
            %   {"tick"="timestamp", "bid"=bid, "ask"=ask}
            % where timestamp is a GMT time and bid and ask are numbers.
            if obj.Endit
                data = [];
                delete(obj.Figure);
            end
            first = obj.CurrentLength + 1;
            [len,stop] = obj.putData@matlab.net.http.io.StringConsumer(data);
            if isempty(data) || stop
                if ischar(data)       % data == '' means user ctrl/c'ed, so set to
                    obj.Endit = true; % delete figure on next close
                end
                stop = true;
            else
                stop = false;
                last = obj.CurrentLength;
                newData = obj.Response.Body.Data.extractBetween(first,last);
                % split at CRLFs
                strings = strsplit(newData, '\r\n');
                try
                    cellfun(@obj.displayJSON, strings);
                catch e
                    fprintf('Error on JSON:\n%s<EOF>\n',data);
                    obj.Endit = true;
                    rethrow(e);
                end
            end
        end
        
        function displayJSON(obj, str)
            if ~isempty(str)
                try
                    val = jsondecode(str);
                catch e
                    fprintf('Error "%s" on JSON:\n%s<EOF>\n',e.message,str);
                    rethrow(e);
                end
                if isfield(val,'tick')
                    tick = val.tick;
                    newdata = {cvtime(val.tick.time),tick.bid,tick.ask};
                    setExtent = ~obj.HaveTick;
                    obj.HaveTick = true;
                elseif isfield(val, 'heartbeat')
                    newdata = {cvtime(val.heartbeat.time),'',''};
                    setExtent = false;
                end
                obj.Table.Data = [newdata;obj.Table.Data];
                if setExtent || ~mod(log10(length(obj.Table.Data)),1)
                    % set extent on first tick and every power of 10
                    % add 15 for width of scroll bar
                    obj.Table.Position(3) = obj.Table.Extent(3) + 15;
                end
                drawnow
            end
        end
        
        function endit(obj,~,~)
            % endit callback from close(obj.Figure)
            if exist('obj','var') && isvalid(obj)
                if obj.Endit
                    if isvalid(obj.Figure)
                        delete(obj.Figure);
                    end
                else
                    obj.Endit = true;
                end
            end
        end
        
        function delete(obj)
            if ~isempty(obj.Figure) && isvalid(obj.Figure)
                delete(obj.Figure);
            end
        end
    end
end

function time = cvtime(time)
% Format time data for display
time = datetime(time,'InputFormat','yyyy-MM-dd''T''HH:mm:ss.S''Z''','TimeZone','GMT');
time.TimeZone = 'local';
time = char(time, 'dd-MMM-yyyy HH:mm:ss.S');
end

MATLAB uitable オブジェクトへのデータのマッピング

Web サービスの API 情報を確認して、ユース ケースに合わせたデータ構造体を特定します。この例のデータには、CRLF で区切られた 1 つ以上の JSON 構造体が含まれます。構造体の形式は以下のいずれかです。ここで、timestamp は GMT 時間、bidask は数値です。

  • {"heartbeat"="timestamp"}

  • {"tick"="timestamp", "bid"=bid, "ask"=ask}

この特定の形式を読み取るには、putData メソッドをオーバーライドします。PricesStreamer クラスの次のステートメントは、StringConsumer.putData を使用して次のバッファーを読み取ってから、JSON 文字列を選択します。

first = obj.CurrentLength + 1;
[len,stop] = obj.putData@matlab.net.http.io.StringConsumer(data);
last = obj.CurrentLength;
newData = obj.Response.Body.Data.extractBetween(first,last);
% split at CRLFs
strings = strsplit(newData, '\r\n');

JSON 形式によるデータの表示

関数 displayJSON の次のステートメントは、JSON の tick および heartbeat 構造体を個別に処理します。補助関数 cvtime は、table で表示するために時刻データの形式を整えます。

function displayJSON(obj, str)
...
val = jsondecode(str);
if isfield(val,'tick')
    tick = val.tick;
    newdata = {cvtime(val.tick.time),tick.bid,tick.ask};
    ...
elseif isfield(val, 'heartbeat')
    newdata = {cvtime(val.heartbeat.time),'',''};
    ...
end
obj.Table.Data = [newdata;obj.Table.Data];
...
end

データ ストリームの終了

この例では、MATLAB は Web サービスがアクティブである間データを受け取り続けます。ユーザーは Figure ウィンドウを閉じるか Ctrl+C を押して、ストリームを終了できます。ユーザーによる割り込みを MATLAB に通知するには、putData の引数 stopfalse に設定します。クリーンアップ タスクには、CloseRequestFcn プロパティを使用して Figure を閉じることや、関数 PricesStreamer.delete を使用してオブジェクトを削除することが含まれます。

PricesStreamer の呼び出し

次のコードは、Web サービスからデータを取得するためのフレームワークを提供します。コードを実行するには、<> 文字に囲まれた内容の値を指定しなければなりません。Web サービスの URL には、ログイン情報やその他の情報を名前と値のペア引数で指定する追加パラメーターが含まれる場合があります。PricesStreamer を活用するには、これを send の呼び出しに追加します。要求メッセージ作成の詳細については、HTTP を使用した MATLAB からの Web サービスの呼び出しを参照してください。

url = matlab.net.URI('<URL>','accountId',<YOUR_ACCOUNT_ID>,'<NAME>','<VALUE>');
authInfo = matlab.net.http.AuthInfo(matlab.net.http.AuthenticationScheme.Bearer,...
    'Encoded','<YOUR_CREDENTIALS>');
af = matlab.net.http.field.AuthorizationField('Authorization',authInfo);
r = matlab.net.http.RequestMessage('get',af);
consumer = PricesStreamer;
% SavePayload set to retain all results - useful for debugging
[resp,req,hist] = r.send(url,matlab.net.http.HTTPOptions('SavePayload',true),consumer);
% Show the results for debugging
show(resp)

以下は、MATLAB uitable オブジェクトへのデータのマッピングで説明されているデータを送信する Web サービスからのデータの例です。

HTTP/1.1 200 Ok
Server: openresty/1.9.15.1
Date: Wed, 06 Sep 2017 19:26:56 GMT
Content-Type: application/json
Transfer-Encoding: chunked
Connection: close
Access-Control-Allow-Origin: *

{"tick":{"instrument":"AUD_CAD","time":"2017-09-06T19:26:54.304054Z","bid":0.97679,"ask":0.97703}}
{"heartbeat":{"time":"2017-09-06T19:26:56.253091Z"}}
{"tick":{"instrument":"AUD_CAD","time":"2017-09-06T19:26:57.226918Z","bid":0.97678,"ask":0.97703}}
{"tick":{"instrument":"AUD_CAD","time":"2017-09-06T19:26:58.226909Z","bid":0.97678,"ask":0.97705}}
{"heartbeat":{"time":"2017-09-06T19:26:58.720409Z"}}
{"tick":{"instrument":"AUD_CAD","time":"2017-09-06T19:27:00.733194Z","bid":0.97679,"ask":0.97704}}
{"heartbeat":{"time":"2017-09-06T19:27:01.251202Z"}}
{"tick":{"instrument":"AUD_CAD","time":"2017-09-06T19:27:01.757501Z","bid":0.9768,"ask":0.97706}}
{"heartbeat":{"time":"2017-09-06T19:27:03.720469Z"}}

参考

|