%

% Copyright 2012 The MathWorks, Inc.

classdef AsymmetricConductor < coder.rtiostreamtest.TestDecorator
%ASYMMETRICCONDUCTOR does the full asymmetric bandwidth test
% The enclosed test must have exactly one output to its runTest
% method. Invokes the enclosed test with parameters which are all the
% combinations of the supplied pcChunks/targChunks array elements and
% generates an array with the results. If producePlot is disabled (it
% is disabled by default), symmetric cases will be skipped.
%
% Parameters:
%
% pcChunks -
% An array of all the sizes that you would require the pc to
% send/receive in one go.
% targChunks -
% An array of all the sizes that you would require the target
% to receive/send in one go.
% verbose -
% Whether or not verbose output is enabled.
% produce_plot -
% If false symmetric cases (where pcChunk == targChunk) will
% not be tested (default behaviour)

% Available parameters defined here
properties (Constant)
param_pcChunks = 'pcChunks';
param_targChunks = 'targChunks';
param_verbose = 'verbose';
param_producePlot = coder.rtiostreamtest.AsymmetricTest.param_producePlot;

% Determines how much data to send. 32 would mean that it will
% transmit 32 times the maximum value among the input arrays or 32
% times the maximum value of the current PC chunk/target chunk
% depending on whether producing of a plot is enabled
data_multiplier = 2;

default_verbose = false;
default_producePlot = false;
end

methods

function [pcChunks, targChunks, verbose, noSymmetric] = getParameters(~, parameters)
% GETPARAMETERS parse parameters into local variables

import coder.rtiostreamtest.*;

pcChunks = Test.getSetting(parameters, AsymmetricConductor.param_pcChunks);
targChunks = Test.getSetting(parameters, AsymmetricConductor.param_targChunks);
verbose = Test.getSetting(parameters, AsymmetricConductor.param_verbose, AsymmetricConductor.default_verbose);
noSymmetric=~Test.getSetting(parameters, AsymmetricConductor.param_producePlot, AsymmetricConductor.default_producePlot);
end

function this = AsymmetricConductor(tester)
this = this@coder.rtiostreamtest.TestDecorator(tester);
end

function [X, Y, Z] = runTest(this, connection, parameters)
% RUNTEST run the profiling tests and returns Z which contains
% the bandwidth for the different pcChunk/targetChunk pairs. X
% contains the pcChunks and Y contains the targChunks.

% Parse the parameters into local variables
[pcChunks, targChunks_raw, verbose, noSymmetric] = this.getParameters(parameters);

% Eliminate values that will cause overflow of the buffer
% available on the target
targChunks = targChunks_raw(targChunks_raw <= connection.targetBufferSize);

% Show warning if some of the values were dropped because of
% insufficient buffer onto the target
if numel(targChunks) ~= numel(targChunks_raw) && verbose
coder.rtiostreamtest.Test.warning_mild('WarningMildSubtestsSkipped', ...
numel(pcChunks) * (numel(targChunks_raw) - numel(targChunks)), ...
connection.targetBufferSize, max(targChunks_raw), max(targChunks_raw));
end

% Print verbose output
coder.rtiostreamtest.Test.lnverboseln(verbose, 'VerboseAsymmPcChunkSizes', coder.rtiostreamtest.AsymmetricConductor.previewArray(pcChunks), numel(pcChunks));
coder.rtiostreamtest.Test.verboseln(verbose, 'VerboseAsymmTargChunkSizes', coder.rtiostreamtest.AsymmetricConductor.previewArray(targChunks), numel(targChunks));

% Assign values to the return variables
X = pcChunks;
Y = targChunks;
Z = this.doAllTests(connection, pcChunks, targChunks, parameters, noSymmetric);
end

function Z = doAllTests(this, connection, pcChunks, targChunks, parameters, noSymmetric)
% DOALLTESTS given parameters, goes through all combinations of
% pc and target chunks and invokes doOneTest for each pair

% Shorthands
targChunksSize = numel(targChunks);
pcChunksSize = numel(pcChunks);

% Calculate the amount of data to be transferred
size = this.data_multiplier * max([pcChunks targChunks]);

% Initialize the return array
Z = zeros(targChunksSize, pcChunksSize);

% Do a test for all combinations
for tg = 1 : targChunksSize
for pc = 1 : pcChunksSize

% Skip symmetric cases if required
if pcChunks(pc) == targChunks(tg) && noSymmetric
continue;
end

% Don't send maximum element * 32 if we don't require a
% plot
if noSymmetric
size = max(pcChunks(pc), targChunks(tg)) * this.data_multiplier;
end

% Calculate the bandwidth for the current
% pcChunk/targChunk pair
Z(tg, pc) = this.doOneTest( ...
connection, pcChunks(pc), targChunks(tg), ...
size, parameters);
end
end

end

function bandwidth = doOneTest(this, connection, pcChunkSize, tgChunkSize, size, parameters)
% DOONETEST conducts one test for a given pcChunk/targChunk pair

import coder.rtiostreamtest.*;

% Call run test setting the parameters that could be required
% by the enclosed test
bandwidth = this.test.runTest(connection, [{...
BandwidthOutgoingTest.param_pcSize, round(pcChunkSize), ...
BandwidthOutgoingTest.param_targetSize, round(tgChunkSize), ...
BandwidthOutgoingTest.param_size, round(size), ...
} parameters]);
end

end

methods (Static)

function result = previewArray(array)
% PREVIEWARRAY prints the array in a verbose format

if numel(array) == 0
% The array is empty
result = '[]';

elseif numel(array) <= 6
% We can print the whole array
result = sprintf('[%d', array(1));

for i = 2 : numel(array)
result = ...
[result, sprintf(', %d', array(i))]; %#ok<AGROW>
end

result = [result ']'];

else
% Print the first two and the last two elements if the
% array is too big
n = numel(array);

result = sprintf('[%d, %d ... %d, %d]', ...
array(1), array(2), array(n-1), array(n));

end
end

end

end