Main Content

Display Streamed Data in Figure Window

This example shows how to customize an HTTP StringConsumer class—PricesStreamer—to display streamed data from a hypothetical website in a MATLAB® figure window. To create a working example:

  • Identify a URL, similar to:

    url = matlab.net.URI('<URL>','accountId',<YOUR_ACCOUNT_ID>,'<NAME>','<VALUE>');
  • Modify PricesStreamer.putData to read data specific to your web service

The following tasks are described in this topic. For information about displaying table data in a figure window, see uitable.

PricesStreamer Class

PricesStreamer.m is a subclass of the StringConsumer class, which is a subclass of ContentConsumer. PricesStreamer receives streamed data customized to the data provided by a specific web service. In this example, the structure of the data is:

% 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 calls the PricesStreamer.putData function for each chunk of data received from the server. The function first converts the raw uint8 bytes to a JSON string using StringConsumer. Next, it gets a MATLAB structure from the JSON string using jsondecode and then displays the data in a table in a figure, adding one line to the top of the table for each increment of data. You can modify the putData function to do something else with the data, for example, plot a real-time graph or display delta prices. PricesStreamer sets the stop return value to stop the operation when the user closes the figure. For more information, see 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

Map Data to MATLAB uitable Object

Identify the data structures for your use case by reading API information from the web service. The data for this example contains one or more CRLF-separated JSON structures. The format for the structures is one of the following, where timestamp is a GMT time and bid and ask are numbers.

  • {"heartbeat"="timestamp"}

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

To read this specific format, override the putData method. The following statements from the PricesStreamer class use StringConsumer.putData to read the next buffer, then select the JSON strings.

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');

Display Data in JSON Format

The following statements from the displayJSON function individually handle the JSON tick and heartbeat structures. A helper function cvtime formats the time data for display in the 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

Terminate Data Stream

In this example, MATLAB receives data as long as the web service is active. The user can terminate the stream by closing the figure window or by pressing Ctrl+C. To inform MATLAB of a user interruption, set the stop argument in putData to false. Clean-up tasks include closing the figure using the CloseRequestFcn property and deleting the object using the PricesStreamer.delete function.

Call PricesStreamer

The following code provides a framework for retrieving data from a web service. To run the code, you must provide values for content within <> characters. The URL for your web service might include additional parameters, such as login information and other information specified as name, value pair arguments. To utilize the PricesStreamer, add it to your call to send. For information about creating request messages, see Call Web Services from MATLAB Using HTTP.

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)

The following is an example of data from a web service sending data described in Map Data to MATLAB uitable Object.

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"}}

See Also

|